Merge branch 'master' of github.com:roundcube/roundcubemail
2 files renamed
17 files deleted
42 files added
74 files modified
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Fix focus on the list when list row is clicked (#1488600) |
| | | - Added separate From and To columns apart from smart From/To column (#1486891) |
| | | - Fix fallback to Larry skin when configured skin isn't available (#1488591) |
| | | - Fix (workaround) delete operations with some versions of memcache (#1488592) |
| | | - Fix (disable) request validation for spell and spell_html actions |
| | | - 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) |
| | | - Fix message display page issues - unified with message preview (#1488590, #1488642) |
| | | - Fix displaying all headers when they contain malformed characters (#1488666) |
| | | - Fix decoding of HTML messages with UTF-16 charset specified (#1488654) |
| | | - Fix quota capability detection so it can be overwritten by a plugin (#1488655) |
| | | - Added template object 'frame' |
| | | - Fix identity selection on reply (#1488101) |
| | | - Add option to enable HTML editor on forwarding (#1488517) |
| | | - Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149) |
| | | - Fix Larry's messages list filter in IE (#1488632) |
| | | - Fix more IE issues by disabling Compat. mode with X-UA-Compatible meta tag (#1488626) |
| | | - Fix setting locales under Solaris - use additional .UTF-8 suffix (#1488628) |
| | | - Fix email address validation for addresses with IP address in domain part |
| | | - Fix Larry skin issues in IE7 compat. mode (#1488618) |
| | | - Fix so subscribed non-existing/non-accessible shared folder can be unsubscribed |
| | | - Added session_path config option and unified cookies settings in javascript |
| | | - Added "Undeleted" option to messages list filter |
| | | - Rewritten test scripts for PHPUnit |
| | | - Add new DB abstraction layer based on PHP PDO, supporting SQLite3 (#1488332) |
| | | - Removed PEAR::MDB2 package |
| | | - Removed users.alias column, added option ('user_aliases') |
| | |
| | | Move global functions from main.inc and rcube_shared.inc into classes |
| | | Better classes separation |
| | | |
| | | RELEASE 0.8.1 |
| | | ------------- |
| | | - Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593) |
| | | - Fix lower-casing email address on replies (#1488598) |
| | | - Fix line separator in exported messages (#1488603) |
| | | - Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613) |
| | | - Fix XSS issue where href="javascript:" wasn't secured (#1488613) |
| | | - Fix impossible to create message with empty plain text part (#1488610) |
| | | - Fix stripped apostrophes when replying in plain text to HTML message (#1488606) |
| | | - Fix inactive Save search option after advanced search (#1488607) |
| | | - Fix Remove from group option is active for contact search result (#1488608) |
| | | - Disable autocapitalization in login form on iPad/iPhone (#1488609) |
| | | - Fix focus on the list when list row is clicked (#1488600) |
| | | - Added separate From and To columns apart from smart From/To column (#1486891) |
| | | - Fix fallback to Larry skin when configured skin isn't available (#1488591) |
| | | - Fix (workaround) delete operations with some versions of memcache (#1488592) |
| | | - Fix (disable) request validation for spell and spell_html actions |
| | | |
| | | RELEASE 0.8.0 |
| | | ------------- |
| | | - Don't show product version on login screen (can be enabled by config) |
| | |
| | | // LOGGING/DEBUGGING |
| | | // ---------------------------------- |
| | | |
| | | // system error reporting: 1 = log; 2 = report (not implemented yet), 4 = show, 8 = trace |
| | | // system error reporting, sum of: 1 = log; 4 = show, 8 = trace |
| | | $rcmail_config['debug_level'] = 1; |
| | | |
| | | // log driver: 'syslog' or 'file'. |
| | |
| | | // 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; |
| | | |
| | |
| | | // 0 - disabled, 1 - username and host only, 2 - username, host, password |
| | | $rcmail_config['login_autocomplete'] = 0; |
| | | |
| | | // If users authentication is not case sensitive this must be enabled. |
| | | // You can also use it to force conversion of logins to lower case. |
| | | // Forces conversion of logins to lower case. |
| | | // 0 - disabled, 1 - only domain part, 2 - domain and local part. |
| | | // If users authentication is not case-sensitive this must be enabled. |
| | | // After enabling it all user records need to be updated, e.g. with query: |
| | | // UPDATE users SET username = LOWER(username); |
| | | $rcmail_config['login_lc'] = false; |
| | | $rcmail_config['login_lc'] = 0; |
| | | |
| | | // Includes should be interpreted as PHP files |
| | | $rcmail_config['skin_include_php'] = false; |
| | |
| | | // must be greater than 'keep_alive'/60 |
| | | $rcmail_config['session_lifetime'] = 10; |
| | | |
| | | // session domain: .example.org |
| | | // Session domain: .example.org |
| | | $rcmail_config['session_domain'] = ''; |
| | | |
| | | // session name. Default: 'roundcube_sessid' |
| | | // Session name. Default: 'roundcube_sessid' |
| | | $rcmail_config['session_name'] = null; |
| | | |
| | | // Session path. Defaults to PHP session.cookie_path setting. |
| | | $rcmail_config['session_path'] = null; |
| | | |
| | | // Backend to use for session storage. Can either be 'db' (default) or 'memcache' |
| | | // If set to memcache, a list of servers need to be specified in 'memcache_hosts' |
| | |
| | | $rcmail_config['show_images'] = 0; |
| | | |
| | | // compose html formatted messages by default |
| | | // 0 - never, 1 - always, 2 - on reply to HTML message only |
| | | // 0 - never, 1 - always, 2 - on reply to HTML message, 3 - on forward or reply to HTML message |
| | | $rcmail_config['htmleditor'] = 0; |
| | | |
| | | // show pretty dates as standard |
| | |
| | | // 2 - Expand only threads with unread messages |
| | | $rcmail_config['autoexpand_threads'] = 0; |
| | | |
| | | // When replying place cursor above original message (top posting) |
| | | $rcmail_config['top_posting'] = false; |
| | | // When replying: |
| | | // -1 - don't cite the original message |
| | | // 0 - place cursor below the original message |
| | | // 1 - place cursor above original message (top posting) |
| | | $rcmail_config['reply_mode'] = 0; |
| | | |
| | | // When replying strip original signature from message |
| | | $rcmail_config['strip_existing_sig'] = true; |
| | |
| | | |
| | | var $obsolete_config = array('db_backend', 'double_auth'); |
| | | var $replaced_config = array( |
| | | 'skin_path' => 'skin', |
| | | 'locale_string' => 'language', |
| | | 'multiple_identities' => 'identities_level', |
| | | 'skin_path' => 'skin', |
| | | 'locale_string' => 'language', |
| | | 'multiple_identities' => 'identities_level', |
| | | 'addrbook_show_images' => 'show_images', |
| | | 'imap_root' => 'imap_ns_personal', |
| | | 'pagesize' => 'mail_pagesize', |
| | | 'imap_root' => 'imap_ns_personal', |
| | | 'pagesize' => 'mail_pagesize', |
| | | 'default_imap_folders' => 'default_folders', |
| | | 'top_posting' => 'reply_mode', |
| | | ); |
| | | |
| | | // these config options are required for a working system |
| | |
| | | |
| | | // Advanced rights |
| | | $attrib['id'] = 'advancedrights'; |
| | | foreach ($supported as $val) { |
| | | $id = "acl$val"; |
| | | foreach ($supported as $idx => $val) { |
| | | $ul .= html::tag('li', null, |
| | | $input->show('', array( |
| | | 'name' => "acl[$val]", 'value' => $val, 'id' => $id)) |
| | |
| | | { |
| | | margin-left: 0.5em; |
| | | } |
| | | |
| | | ul.toolbarmenu li span.delete { |
| | | background-position: 0 -1509px; |
| | | } |
| | |
| | | <roundcube:object name="acltable" id="acltable" class="records-table" /> |
| | | </div> |
| | | <div id="acllist-footer" class="boxfooter"> |
| | | <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu');return false" innerClass="inner" content="⚙" /> |
| | | <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu', undefined, {above:1});return false" innerClass="inner" content="⚙" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div id="aclmenu" class="popupmenu"> |
| | | <ul class="toolbarmenu selectable"> |
| | | <li><roundcube:button command="acl-edit" label="edit" classAct="active" /></li> |
| | | <li><roundcube:button command="acl-delete" label="delete" classAct="active" /></li> |
| | | <ul class="toolbarmenu selectable iconized"> |
| | | <li><roundcube:button command="acl-edit" label="edit" class="icon" classAct="icon active" innerclass="icon edit" /></li> |
| | | <li><roundcube:button command="acl-delete" label="delete" class="icon" classAct="icon active" innerclass="icon delete" /></li> |
| | | <roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" /> |
| | | <li><roundcube:button name="acl-switch" id="acl-switch" label="acl.advanced" onclick="rcmail.command('acl-mode-switch')" class="active" /></li> |
| | | <roundcube:endif /> |
| | |
| | | && ($limit = $rcmail->config->get('hide_blockquote_limit')) |
| | | ) { |
| | | // include styles |
| | | $skin = $rcmail->config->get('skin'); |
| | | if (!file_exists($this->home."/skins/$skin/style.css")) { |
| | | $skin = 'default'; |
| | | } |
| | | $this->include_stylesheet("skins/$skin/style.css"); |
| | | $this->include_stylesheet($this->local_skin_path() . "/style.css"); |
| | | |
| | | // Script and localization |
| | | $this->include_script('hide_blockquote.js'); |
New file |
| | |
| | | <?php |
| | | |
| | | class Parser extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | function setUp() |
| | | { |
| | | include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php'; |
| | | } |
| | | |
| | | /** |
| | | * Sieve script parsing |
| | | * |
| | | * @dataProvider data_parser |
| | | */ |
| | | function test_parser($input, $output, $message) |
| | | { |
| | | $script = new rcube_sieve_script($input); |
| | | $result = $script->as_text(); |
| | | |
| | | $this->assertEquals(trim($result), trim($output), $message); |
| | | } |
| | | |
| | | /** |
| | | * Data provider for test_parser() |
| | | */ |
| | | function data_parser() |
| | | { |
| | | $dir_path = realpath(dirname(__FILE__) . '/src'); |
| | | $dir = opendir($dir_path); |
| | | $result = array(); |
| | | |
| | | while ($file = readdir($dir)) { |
| | | if (preg_match('/^[a-z0-9_]+$/', $file)) { |
| | | $input = file_get_contents($dir_path . '/' . $file); |
| | | |
| | | if (file_exists($dir_path . '/' . $file . '.out')) { |
| | | $output = file_get_contents($dir_path . '/' . $file . '.out'); |
| | | } |
| | | else { |
| | | $output = $input; |
| | | } |
| | | |
| | | $result[] = array( |
| | | 'input' => $input, |
| | | 'output' => $output, |
| | | 'message' => "Error in parsing '$file' file", |
| | | ); |
| | | } |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | class Tokenizer extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | function setUp() |
| | | { |
| | | include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php'; |
| | | } |
| | | |
| | | function data_tokenizer() |
| | | { |
| | | return array( |
| | | array(1, "text: #test\nThis is test ; message;\nMulti line\n.\n;\n", '"This is test ; message;\nMulti line"'), |
| | | array(0, '["test1","test2"]', '[["test1","test2"]]'), |
| | | array(1, '["test"]', '["test"]'), |
| | | array(1, '"te\\"st"', '"te\\"st"'), |
| | | array(0, 'test #comment', '["test"]'), |
| | | array(0, "text:\ntest\n.\ntext:\ntest\n.\n", '["test","test"]'), |
| | | array(1, '"\\a\\\\\\"a"', '"a\\\\\\"a"'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_tokenizer |
| | | */ |
| | | function test_tokenizer($num, $input, $output) |
| | | { |
| | | $res = json_encode(rcube_sieve_script::tokenize($input, $num)); |
| | | |
| | | $this->assertEquals(trim($res), trim($output)); |
| | | } |
| | | } |
New file |
| | |
| | | require ["fileinto","reject","envelope"]; |
| | | # rule:[spam] |
| | | if anyof (header :contains "X-DSPAM-Result" "Spam") |
| | | { |
| | | fileinto "Spam"; |
| | | stop; |
| | | } |
| | | # rule:[test1] |
| | | if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld") |
| | | { |
| | | discard; |
| | | stop; |
| | | } |
| | | # rule:[test2] |
| | | if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]") |
| | | { |
| | | fileinto "test"; |
| | | stop; |
| | | } |
| | | # rule:[comments] |
| | | if anyof (true) /* comment |
| | | * "comment" #comment */ { |
| | | /* comment */ stop; |
| | | # comment |
| | | } |
| | | # rule:[reject] |
| | | if size :over 5000K { |
| | | reject "Message over 5MB size limit. Please contact me before sending this."; |
| | | } |
| | | # rule:[false] |
| | | if false # size :over 5000K |
| | | { |
| | | stop; /* rule disabled */ |
| | | } |
| | | # rule:[true] |
| | | if true |
| | | { |
| | | stop; |
| | | } |
| | | fileinto "Test"; |
| | | # rule:[address test] |
| | | if address :all :is "From" "nagios@domain.tld" |
| | | { |
| | | fileinto "domain.tld"; |
| | | stop; |
| | | } |
| | | # rule:[envelope test] |
| | | if envelope :domain :is "From" "domain.tld" |
| | | { |
| | | fileinto "domain.tld"; |
| | | stop; |
| | | } |
New file |
| | |
| | | require ["fileinto","reject","envelope"]; |
| | | # rule:[spam] |
| | | if header :contains "X-DSPAM-Result" "Spam" |
| | | { |
| | | fileinto "Spam"; |
| | | stop; |
| | | } |
| | | # rule:[test1] |
| | | if header :contains ["From","To"] "test@domain.tld" |
| | | { |
| | | discard; |
| | | stop; |
| | | } |
| | | # rule:[test2] |
| | | if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]") |
| | | { |
| | | fileinto "test"; |
| | | stop; |
| | | } |
| | | # rule:[comments] |
| | | if true |
| | | { |
| | | stop; |
| | | } |
| | | # rule:[reject] |
| | | if size :over 5000K |
| | | { |
| | | reject "Message over 5MB size limit. Please contact me before sending this."; |
| | | } |
| | | # rule:[false] |
| | | if false # size :over 5000K |
| | | { |
| | | stop; |
| | | } |
| | | # rule:[true] |
| | | if true |
| | | { |
| | | stop; |
| | | } |
| | | fileinto "Test"; |
| | | # rule:[address test] |
| | | if address :all :is "From" "nagios@domain.tld" |
| | | { |
| | | fileinto "domain.tld"; |
| | | stop; |
| | | } |
| | | # rule:[envelope test] |
| | | if envelope :domain :is "From" "domain.tld" |
| | | { |
| | | fileinto "domain.tld"; |
| | | stop; |
| | | } |
New file |
| | |
| | | require ["body","fileinto"]; |
| | | if body :raw :contains "MAKE MONEY FAST" |
| | | { |
| | | stop; |
| | | } |
| | | if body :content "text" :contains ["missile","coordinates"] |
| | | { |
| | | fileinto "secrets"; |
| | | } |
| | | if body :content "audio/mp3" :contains "" |
| | | { |
| | | fileinto "jukebox"; |
| | | } |
| | | if body :text :contains "project schedule" |
| | | { |
| | | fileinto "project/schedule"; |
| | | } |
New file |
| | |
| | | require ["imap4flags"]; |
| | | # rule:[imapflags] |
| | | if header :matches "Subject" "^Test$" |
| | | { |
| | | setflag "\\Seen"; |
| | | addflag ["\\Answered","\\Deleted"]; |
| | | } |
New file |
| | |
| | | require ["include"]; |
| | | include "script.sieve"; |
| | | # rule:[two] |
| | | if true |
| | | { |
| | | include :optional "second.sieve"; |
| | | } |
New file |
| | |
| | | # EDITOR Roundcube |
| | | # EDITOR_VERSION 123 |
New file |
| | |
| | | require ["variables"]; |
| | | set "EDITOR" "Roundcube"; |
| | | set "EDITOR_VERSION" "123"; |
New file |
| | |
| | | # this is a comment |
| | | # and the second line |
| | | |
| | | require ["variables"]; |
| | | set "b" "c"; |
New file |
| | |
| | | require ["relational","comparator-i;ascii-numeric"]; |
| | | # rule:[redirect] |
| | | if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14" |
| | | { |
| | | redirect "test@test.tld"; |
| | | } |
New file |
| | |
| | | require ["envelope","subaddress","fileinto"]; |
| | | if envelope :user "To" "postmaster" |
| | | { |
| | | fileinto "postmaster"; |
| | | stop; |
| | | } |
| | | if envelope :detail :is "To" "mta-filters" |
| | | { |
| | | fileinto "mta-filters"; |
| | | stop; |
| | | } |
New file |
| | |
| | | require ["vacation"]; |
| | | # rule:[test-vacation] |
| | | if header :contains "Subject" "vacation" |
| | | { |
| | | vacation :days 1 text: |
| | | # test |
| | | test test /* test */ |
| | | test |
| | | . |
| | | ; |
| | | stop; |
| | | } |
New file |
| | |
| | | require ["variables"]; |
| | | set "honorific" "Mr"; |
| | | set "vacation" text: |
| | | Dear ${HONORIFIC} ${last_name}, |
| | | I am out, please leave a message after the meep. |
| | | . |
| | | ; |
| | | set :length "b" "${a}"; |
| | | set :lower "b" "${a}"; |
| | | set :upperfirst "b" "${a}"; |
| | | set :upperfirst :lower "b" "${a}"; |
| | | set :quotewildcard "b" "Rock*"; |
| | |
| | | $pieces = explode("_", $username); |
| | | $domain = $pieces[0]; |
| | | break; |
| | | case 8: // domain taken from alias, username left as it was |
| | | $email = $rcmail->user->data['alias']; |
| | | $domain = substr(strrchr($email, "@"), 1); |
| | | break; |
| | | default: // username@domain |
| | | $domain = substr(strrchr($username, "@"), 1); |
| | | } |
| | |
| | | * |
| | | * $rcmail_config['virtuser_query'] = array('email' => '', 'user' => '', 'host' => ''); |
| | | * |
| | | * The email query can return more than one record to create more identities. |
| | | * This requires identities_level option to be set to value less than 2. |
| | | * |
| | | * @version @package_version@ |
| | | * @author Aleksander Machniak |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | * @author Steffen Vogel |
| | | */ |
| | | class virtuser_query extends rcube_plugin |
| | |
| | | */ |
| | | function get_opt($aliases = array()) |
| | | { |
| | | $args = array(); |
| | | for ($i=1; $i < count($_SERVER['argv']); $i++) { |
| | | $arg = $_SERVER['argv'][$i]; |
| | | $value = true; |
| | | $key = null; |
| | | $args = array(); |
| | | |
| | | if ($arg[0] == '-') { |
| | | $key = preg_replace('/^-+/', '', $arg); |
| | | $sp = strpos($arg, '='); |
| | | if ($sp > 0) { |
| | | $key = substr($key, 0, $sp - 2); |
| | | $value = substr($arg, $sp+1); |
| | | } |
| | | else if (strlen($_SERVER['argv'][$i+1]) && $_SERVER['argv'][$i+1][0] != '-') { |
| | | $value = $_SERVER['argv'][++$i]; |
| | | } |
| | | for ($i=1; $i < count($_SERVER['argv']); $i++) { |
| | | $arg = $_SERVER['argv'][$i]; |
| | | $value = true; |
| | | $key = null; |
| | | |
| | | $args[$key] = is_string($value) ? preg_replace(array('/^["\']/', '/["\']$/'), '', $value) : $value; |
| | | } |
| | | else |
| | | $args[] = $arg; |
| | | if ($arg[0] == '-') { |
| | | $key = preg_replace('/^-+/', '', $arg); |
| | | $sp = strpos($arg, '='); |
| | | if ($sp > 0) { |
| | | $key = substr($key, 0, $sp - 2); |
| | | $value = substr($arg, $sp+1); |
| | | } |
| | | else if (strlen($_SERVER['argv'][$i+1]) && $_SERVER['argv'][$i+1][0] != '-') { |
| | | $value = $_SERVER['argv'][++$i]; |
| | | } |
| | | |
| | | if ($alias = $aliases[$key]) |
| | | $args[$alias] = $args[$key]; |
| | | } |
| | | $args[$key] = is_string($value) ? preg_replace(array('/^["\']/', '/["\']$/'), '', $value) : $value; |
| | | } |
| | | else { |
| | | $args[] = $arg; |
| | | } |
| | | |
| | | return $args; |
| | | if ($alias = $aliases[$key]) { |
| | | $args[$alias] = $args[$key]; |
| | | } |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | |
| | |
| | | $attr = array('src' => $attr); |
| | | } |
| | | return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib, |
| | | array('src','alt','width','height','border','usemap','onclick'))); |
| | | array('src','alt','width','height','border','usemap','onclick'))); |
| | | } |
| | | |
| | | /** |
| | |
| | | $attr = array('href' => $attr); |
| | | } |
| | | return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, |
| | | array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); |
| | | array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); |
| | | } |
| | | |
| | | /** |
| | |
| | | protected $tagname = 'input'; |
| | | protected $type = 'text'; |
| | | protected $allowed = array( |
| | | 'type','name','value','size','tabindex', |
| | | 'type','name','value','size','tabindex','autocapitalize', |
| | | 'autocomplete','checked','onchange','onclick','disabled','readonly', |
| | | 'spellcheck','results','maxlength','src','multiple','placeholder', |
| | | ); |
| | |
| | | { |
| | | protected $tagname = 'textarea'; |
| | | protected $allowed = array('name','rows','cols','wrap','tabindex', |
| | | 'onchange','disabled','readonly','spellcheck'); |
| | | 'onchange','disabled','readonly','spellcheck'); |
| | | |
| | | /** |
| | | * Get HTML code for this object |
| | |
| | | } |
| | | |
| | | return self::tag($this->tagname, $this->attrib, $value, |
| | | array_merge(self::$common_attrib, $this->allowed)); |
| | | array_merge(self::$common_attrib, $this->allowed)); |
| | | } |
| | | } |
| | | |
| | |
| | | protected $tagname = 'select'; |
| | | protected $options = array(); |
| | | protected $allowed = array('name','size','tabindex','autocomplete', |
| | | 'multiple','onchange','disabled','rel'); |
| | | 'multiple','onchange','disabled','rel'); |
| | | |
| | | /** |
| | | * Add a new option to this drop-down |
| | |
| | | { |
| | | protected $tagname = 'table'; |
| | | protected $allowed = array('id','class','style','width','summary', |
| | | 'cellpadding','cellspacing','border'); |
| | | 'cellpadding','cellspacing','border'); |
| | | |
| | | private $header = array(); |
| | | private $rows = array(); |
| | |
| | | */ |
| | | public function add_header($attr, $cont) |
| | | { |
| | | if (is_string($attr)) |
| | | $attr = array('class' => $attr); |
| | | if (is_string($attr)) { |
| | | $attr = array('class' => $attr); |
| | | } |
| | | |
| | | $cell = new stdClass; |
| | | $cell->attrib = $attr; |
| | |
| | | */ |
| | | public function set_row_attribs($attr = array(), $index = null) |
| | | { |
| | | if (is_string($attr)) |
| | | $attr = array('class' => $attr); |
| | | if (is_string($attr)) { |
| | | $attr = array('class' => $attr); |
| | | } |
| | | |
| | | if ($index === null) |
| | | if ($index === null) { |
| | | $index = $this->rowindex; |
| | | } |
| | | |
| | | $this->rows[$index]->attrib = $attr; |
| | | } |
| | |
| | | */ |
| | | public function get_row_attribs($index = null) |
| | | { |
| | | if ($index === null) |
| | | if ($index === null) { |
| | | $index = $this->rowindex; |
| | | } |
| | | |
| | | return $this->rows[$index] ? $this->rows[$index]->attrib : null; |
| | | } |
| | |
| | | $this->config->set_user_prefs((array)$this->user->get_prefs()); |
| | | } |
| | | |
| | | $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language'])); |
| | | $lang = $this->language_prop($this->config->get('language', $_SESSION['language'])); |
| | | $_SESSION['language'] = $this->user->language = $lang; |
| | | |
| | | // set localization |
| | | setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8'); |
| | | setlocale(LC_ALL, $lang . '.utf8', $lang . '.UTF-8', 'en_US.utf8', 'en_US.UTF-8'); |
| | | |
| | | // workaround for http://bugs.php.net/bug.php?id=18556 |
| | | if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ'))) |
| | | setlocale(LC_CTYPE, 'en_US' . '.utf8'); |
| | | if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) { |
| | | setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8'); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | /** |
| | | * Init output object for GUI and add common scripts. |
| | | * This will instantiate a rcmail_template object and set |
| | | * This will instantiate a rcube_output_html object and set |
| | | * environment vars according to the current session and configuration |
| | | * |
| | | * @param boolean True if this request is loaded in a (i)frame |
| | |
| | | // Convert username to lowercase. If storage backend |
| | | // is case-insensitive we need to store always the same username (#1487113) |
| | | if ($config['login_lc']) { |
| | | $username = mb_strtolower($username); |
| | | if ($config['login_lc'] == 2 || $config['login_lc'] === true) { |
| | | $username = mb_strtolower($username); |
| | | } |
| | | else if (strpos($username, '@')) { |
| | | // lowercase domain name |
| | | list($local, $domain) = explode('@', $username); |
| | | $username = $local . '@' . mb_strtolower($domain); |
| | | } |
| | | } |
| | | |
| | | // try to resolve email address from virtuser table |
| | |
| | | |
| | | // Here we need IDNA ASCII |
| | | // Only rcube_contacts class is using domain names in Unicode |
| | | $host = rcube_utils::idn_to_ascii($host); |
| | | if (strpos($username, '@')) { |
| | | // lowercase domain name |
| | | list($local, $domain) = explode('@', $username); |
| | | $username = $local . '@' . mb_strtolower($domain); |
| | | $username = rcube_utils::idn_to_ascii($username); |
| | | } |
| | | $host = rcube_utils::idn_to_ascii($host); |
| | | $username = rcube_utils::idn_to_ascii($username); |
| | | |
| | | // user already registered -> overwrite username |
| | | if ($user = rcube_user::query($username, $host)) |
| | | if ($user = rcube_user::query($username, $host)) { |
| | | $username = $user->data['username']; |
| | | } |
| | | |
| | | $storage = $this->get_storage(); |
| | | |
| | |
| | | } |
| | | else { |
| | | if (!empty($date)) { |
| | | $timestamp = rcube_strtotime($date); |
| | | $timestamp = rcube_utils::strtotime($date); |
| | | } |
| | | |
| | | if (empty($timestamp)) { |
| | |
| | | * Base class of the Roundcube Framework |
| | | * implemented as singleton |
| | | * |
| | | * @package Core |
| | | * @package Framework |
| | | * @subpackage Core |
| | | */ |
| | | class rcube |
| | | { |
| | | const INIT_WITH_DB = 1; |
| | | const INIT_WITH_PLUGINS = 2; |
| | | const INIT_WITH_DB = 1; |
| | | const INIT_WITH_PLUGINS = 2; |
| | | |
| | | /** |
| | | * Singleton instace of rcube |
| | | * |
| | | * @var rcmail |
| | | */ |
| | | static protected $instance; |
| | | /** |
| | | * Singleton instace of rcube |
| | | * |
| | | * @var rcmail |
| | | */ |
| | | static protected $instance; |
| | | |
| | | /** |
| | | * Stores instance of rcube_config. |
| | | * |
| | | * @var rcube_config |
| | | */ |
| | | public $config; |
| | | /** |
| | | * Stores instance of rcube_config. |
| | | * |
| | | * @var rcube_config |
| | | */ |
| | | public $config; |
| | | |
| | | /** |
| | | * Instace of database class. |
| | | * |
| | | * @var rcube_pdo |
| | | */ |
| | | public $db; |
| | | /** |
| | | * Instace of database class. |
| | | * |
| | | * @var rcube_db |
| | | */ |
| | | public $db; |
| | | |
| | | /** |
| | | * Instace of Memcache class. |
| | | * |
| | | * @var Memcache |
| | | */ |
| | | public $memcache; |
| | | /** |
| | | * Instace of Memcache class. |
| | | * |
| | | * @var Memcache |
| | | */ |
| | | public $memcache; |
| | | |
| | | /** |
| | | * Instace of rcube_session class. |
| | | * |
| | | * @var rcube_session |
| | | */ |
| | | public $session; |
| | | /** |
| | | * Instace of rcube_session class. |
| | | * |
| | | * @var rcube_session |
| | | */ |
| | | public $session; |
| | | |
| | | /** |
| | | * Instance of rcube_smtp class. |
| | | * |
| | | * @var rcube_smtp |
| | | */ |
| | | public $smtp; |
| | | /** |
| | | * Instance of rcube_smtp class. |
| | | * |
| | | * @var rcube_smtp |
| | | */ |
| | | public $smtp; |
| | | |
| | | /** |
| | | * Instance of rcube_storage class. |
| | | * |
| | | * @var rcube_storage |
| | | */ |
| | | public $storage; |
| | | /** |
| | | * Instance of rcube_storage class. |
| | | * |
| | | * @var rcube_storage |
| | | */ |
| | | public $storage; |
| | | |
| | | /** |
| | | * Instance of rcube_output class. |
| | | * |
| | | * @var rcube_output |
| | | */ |
| | | public $output; |
| | | /** |
| | | * Instance of rcube_output class. |
| | | * |
| | | * @var rcube_output |
| | | */ |
| | | public $output; |
| | | |
| | | /** |
| | | * Instance of rcube_plugin_api. |
| | | * |
| | | * @var rcube_plugin_api |
| | | */ |
| | | public $plugins; |
| | | /** |
| | | * Instance of rcube_plugin_api. |
| | | * |
| | | * @var rcube_plugin_api |
| | | */ |
| | | public $plugins; |
| | | |
| | | |
| | | /* private/protected vars */ |
| | | protected $texts; |
| | | protected $caches = array(); |
| | | protected $shutdown_functions = array(); |
| | | protected $expunge_cache = false; |
| | | /* private/protected vars */ |
| | | protected $texts; |
| | | protected $caches = array(); |
| | | protected $shutdown_functions = array(); |
| | | protected $expunge_cache = false; |
| | | |
| | | |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants |
| | | * @return rcube The one and only instance |
| | | */ |
| | | static function get_instance($mode = 0) |
| | | { |
| | | if (!self::$instance) { |
| | | self::$instance = new rcube(); |
| | | self::$instance->init($mode); |
| | | } |
| | | |
| | | return self::$instance; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private constructor |
| | | */ |
| | | protected function __construct() |
| | | { |
| | | // load configuration |
| | | $this->config = new rcube_config(); |
| | | $this->plugins = new rcube_dummy_plugin_api; |
| | | |
| | | register_shutdown_function(array($this, 'shutdown')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initial startup function |
| | | */ |
| | | protected function init($mode = 0) |
| | | { |
| | | // initialize syslog |
| | | if ($this->config->get('log_driver') == 'syslog') { |
| | | $syslog_id = $this->config->get('syslog_id', 'roundcube'); |
| | | $syslog_facility = $this->config->get('syslog_facility', LOG_USER); |
| | | openlog($syslog_id, LOG_ODELAY, $syslog_facility); |
| | | } |
| | | |
| | | // connect to database |
| | | if ($mode & self::INIT_WITH_DB) { |
| | | $this->get_dbh(); |
| | | } |
| | | |
| | | // create plugin API and load plugins |
| | | if ($mode & self::INIT_WITH_PLUGINS) { |
| | | $this->plugins = rcube_plugin_api::get_instance(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the current database connection |
| | | * |
| | | * @return rcube_pdo Database connection object |
| | | */ |
| | | public function get_dbh() |
| | | { |
| | | if (!$this->db) { |
| | | $config_all = $this->config->all(); |
| | | $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); |
| | | $this->db->set_debug((bool)$config_all['sql_debug']); |
| | | } |
| | | |
| | | return $this->db; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get global handle for memcache access |
| | | * |
| | | * @return object Memcache |
| | | */ |
| | | public function get_memcache() |
| | | { |
| | | if (!isset($this->memcache)) { |
| | | // no memcache support in PHP |
| | | if (!class_exists('Memcache')) { |
| | | $this->memcache = false; |
| | | return false; |
| | | } |
| | | |
| | | $this->memcache = new Memcache; |
| | | $this->mc_available = 0; |
| | | |
| | | // add all configured hosts to pool |
| | | $pconnect = $this->config->get('memcache_pconnect', true); |
| | | foreach ($this->config->get('memcache_hosts', array()) as $host) { |
| | | if (substr($host, 0, 7) != 'unix://') { |
| | | list($host, $port) = explode(':', $host); |
| | | if (!$port) $port = 11211; |
| | | } |
| | | else { |
| | | $port = 0; |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants |
| | | * |
| | | * @return rcube The one and only instance |
| | | */ |
| | | static function get_instance($mode = 0) |
| | | { |
| | | if (!self::$instance) { |
| | | self::$instance = new rcube(); |
| | | self::$instance->init($mode); |
| | | } |
| | | |
| | | $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); |
| | | } |
| | | |
| | | // test connection and failover (will result in $this->mc_available == 0 on complete failure) |
| | | $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist |
| | | |
| | | if (!$this->mc_available) |
| | | $this->memcache = false; |
| | | return self::$instance; |
| | | } |
| | | |
| | | return $this->memcache; |
| | | } |
| | | |
| | | /** |
| | | * Private constructor |
| | | */ |
| | | protected function __construct() |
| | | { |
| | | // load configuration |
| | | $this->config = new rcube_config; |
| | | $this->plugins = new rcube_dummy_plugin_api; |
| | | |
| | | /** |
| | | * Callback for memcache failure |
| | | */ |
| | | public function memcache_failure($host, $port) |
| | | { |
| | | static $seen = array(); |
| | | |
| | | // only report once |
| | | if (!$seen["$host:$port"]++) { |
| | | $this->mc_available--; |
| | | self::raise_error(array('code' => 604, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => "Memcache failure on host $host:$port"), |
| | | true, false); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get cache object |
| | | * |
| | | * @param string $name Cache identifier |
| | | * @param string $type Cache type ('db', 'apc' or 'memcache') |
| | | * @param string $ttl Expiration time for cache items |
| | | * @param bool $packed Enables/disables data serialization |
| | | * |
| | | * @return rcube_cache Cache object |
| | | */ |
| | | public function get_cache($name, $type='db', $ttl=0, $packed=true) |
| | | { |
| | | if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) { |
| | | $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); |
| | | register_shutdown_function(array($this, 'shutdown')); |
| | | } |
| | | |
| | | return $this->caches[$name]; |
| | | } |
| | | |
| | | /** |
| | | * Initial startup function |
| | | */ |
| | | protected function init($mode = 0) |
| | | { |
| | | // initialize syslog |
| | | if ($this->config->get('log_driver') == 'syslog') { |
| | | $syslog_id = $this->config->get('syslog_id', 'roundcube'); |
| | | $syslog_facility = $this->config->get('syslog_facility', LOG_USER); |
| | | openlog($syslog_id, LOG_ODELAY, $syslog_facility); |
| | | } |
| | | |
| | | /** |
| | | * Create SMTP object and connect to server |
| | | * |
| | | * @param boolean True if connection should be established |
| | | */ |
| | | public function smtp_init($connect = false) |
| | | { |
| | | $this->smtp = new rcube_smtp(); |
| | | // connect to database |
| | | if ($mode & self::INIT_WITH_DB) { |
| | | $this->get_dbh(); |
| | | } |
| | | |
| | | if ($connect) |
| | | $this->smtp->connect(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get storage object |
| | | * |
| | | * @return rcube_storage Storage object |
| | | */ |
| | | public function get_storage() |
| | | { |
| | | // already initialized |
| | | if (!is_object($this->storage)) { |
| | | $this->storage_init(); |
| | | // create plugin API and load plugins |
| | | if ($mode & self::INIT_WITH_PLUGINS) { |
| | | $this->plugins = rcube_plugin_api::get_instance(); |
| | | } |
| | | } |
| | | |
| | | return $this->storage; |
| | | } |
| | | |
| | | /** |
| | | * Get the current database connection |
| | | * |
| | | * @return rcube_db Database object |
| | | */ |
| | | public function get_dbh() |
| | | { |
| | | if (!$this->db) { |
| | | $config_all = $this->config->all(); |
| | | $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); |
| | | $this->db->set_debug((bool)$config_all['sql_debug']); |
| | | } |
| | | |
| | | /** |
| | | * Initialize storage object |
| | | */ |
| | | public function storage_init() |
| | | { |
| | | // already initialized |
| | | if (is_object($this->storage)) { |
| | | return; |
| | | return $this->db; |
| | | } |
| | | |
| | | $driver = $this->config->get('storage_driver', 'imap'); |
| | | $driver_class = "rcube_{$driver}"; |
| | | |
| | | if (!class_exists($driver_class)) { |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Storage driver class ($driver) not found!"), |
| | | true, true); |
| | | /** |
| | | * Get global handle for memcache access |
| | | * |
| | | * @return object Memcache |
| | | */ |
| | | public function get_memcache() |
| | | { |
| | | if (!isset($this->memcache)) { |
| | | // no memcache support in PHP |
| | | if (!class_exists('Memcache')) { |
| | | $this->memcache = false; |
| | | return false; |
| | | } |
| | | |
| | | $this->memcache = new Memcache; |
| | | $this->mc_available = 0; |
| | | |
| | | // add all configured hosts to pool |
| | | $pconnect = $this->config->get('memcache_pconnect', true); |
| | | foreach ($this->config->get('memcache_hosts', array()) as $host) { |
| | | if (substr($host, 0, 7) != 'unix://') { |
| | | list($host, $port) = explode(':', $host); |
| | | if (!$port) $port = 11211; |
| | | } |
| | | else { |
| | | $port = 0; |
| | | } |
| | | |
| | | $this->mc_available += intval($this->memcache->addServer( |
| | | $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); |
| | | } |
| | | |
| | | // test connection and failover (will result in $this->mc_available == 0 on complete failure) |
| | | $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist |
| | | |
| | | if (!$this->mc_available) { |
| | | $this->memcache = false; |
| | | } |
| | | } |
| | | |
| | | return $this->memcache; |
| | | } |
| | | |
| | | // Initialize storage object |
| | | $this->storage = new $driver_class; |
| | | |
| | | // for backward compat. (deprecated, will be removed) |
| | | $this->imap = $this->storage; |
| | | /** |
| | | * Callback for memcache failure |
| | | */ |
| | | public function memcache_failure($host, $port) |
| | | { |
| | | static $seen = array(); |
| | | |
| | | // enable caching of mail data |
| | | $storage_cache = $this->config->get("{$driver}_cache"); |
| | | $messages_cache = $this->config->get('messages_cache'); |
| | | // for backward compatybility |
| | | if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { |
| | | $storage_cache = 'db'; |
| | | $messages_cache = true; |
| | | // only report once |
| | | if (!$seen["$host:$port"]++) { |
| | | $this->mc_available--; |
| | | self::raise_error(array( |
| | | 'code' => 604, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => "Memcache failure on host $host:$port"), |
| | | true, false); |
| | | } |
| | | } |
| | | |
| | | if ($storage_cache) |
| | | $this->storage->set_caching($storage_cache); |
| | | if ($messages_cache) |
| | | $this->storage->set_messages_caching(true); |
| | | |
| | | // set pagesize from config |
| | | $pagesize = $this->config->get('mail_pagesize'); |
| | | if (!$pagesize) { |
| | | $pagesize = $this->config->get('pagesize', 50); |
| | | } |
| | | $this->storage->set_pagesize($pagesize); |
| | | /** |
| | | * Initialize and get cache object |
| | | * |
| | | * @param string $name Cache identifier |
| | | * @param string $type Cache type ('db', 'apc' or 'memcache') |
| | | * @param string $ttl Expiration time for cache items |
| | | * @param bool $packed Enables/disables data serialization |
| | | * |
| | | * @return rcube_cache Cache object |
| | | */ |
| | | public function get_cache($name, $type='db', $ttl=0, $packed=true) |
| | | { |
| | | if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) { |
| | | $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); |
| | | } |
| | | |
| | | // set class options |
| | | $options = array( |
| | | 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), |
| | | 'auth_cid' => $this->config->get("{$driver}_auth_cid"), |
| | | 'auth_pw' => $this->config->get("{$driver}_auth_pw"), |
| | | 'debug' => (bool) $this->config->get("{$driver}_debug"), |
| | | 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), |
| | | 'timeout' => (int) $this->config->get("{$driver}_timeout"), |
| | | 'skip_deleted' => (bool) $this->config->get('skip_deleted'), |
| | | 'driver' => $driver, |
| | | ); |
| | | |
| | | if (!empty($_SESSION['storage_host'])) { |
| | | $options['host'] = $_SESSION['storage_host']; |
| | | $options['user'] = $_SESSION['username']; |
| | | $options['port'] = $_SESSION['storage_port']; |
| | | $options['ssl'] = $_SESSION['storage_ssl']; |
| | | $options['password'] = $this->decrypt($_SESSION['password']); |
| | | $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; |
| | | return $this->caches[$name]; |
| | | } |
| | | |
| | | $options = $this->plugins->exec_hook("storage_init", $options); |
| | | |
| | | // for backward compat. (deprecated, to be removed) |
| | | $options = $this->plugins->exec_hook("imap_init", $options); |
| | | /** |
| | | * Create SMTP object and connect to server |
| | | * |
| | | * @param boolean True if connection should be established |
| | | */ |
| | | public function smtp_init($connect = false) |
| | | { |
| | | $this->smtp = new rcube_smtp(); |
| | | |
| | | $this->storage->set_options($options); |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set storage parameters. |
| | | * This must be done AFTER connecting to the server! |
| | | */ |
| | | protected function set_storage_prop() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); |
| | | |
| | | if ($default_folders = $this->config->get('default_folders')) { |
| | | $storage->set_default_folders($default_folders); |
| | | if ($connect) { |
| | | $this->smtp->connect(); |
| | | } |
| | | } |
| | | if (isset($_SESSION['mbox'])) { |
| | | $storage->set_folder($_SESSION['mbox']); |
| | | |
| | | |
| | | /** |
| | | * Initialize and get storage object |
| | | * |
| | | * @return rcube_storage Storage object |
| | | */ |
| | | public function get_storage() |
| | | { |
| | | // already initialized |
| | | if (!is_object($this->storage)) { |
| | | $this->storage_init(); |
| | | } |
| | | |
| | | return $this->storage; |
| | | } |
| | | if (isset($_SESSION['page'])) { |
| | | $storage->set_page($_SESSION['page']); |
| | | |
| | | |
| | | /** |
| | | * Initialize storage object |
| | | */ |
| | | public function storage_init() |
| | | { |
| | | // already initialized |
| | | if (is_object($this->storage)) { |
| | | return; |
| | | } |
| | | |
| | | $driver = $this->config->get('storage_driver', 'imap'); |
| | | $driver_class = "rcube_{$driver}"; |
| | | |
| | | if (!class_exists($driver_class)) { |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Storage driver class ($driver) not found!"), |
| | | true, true); |
| | | } |
| | | |
| | | // Initialize storage object |
| | | $this->storage = new $driver_class; |
| | | |
| | | // for backward compat. (deprecated, will be removed) |
| | | $this->imap = $this->storage; |
| | | |
| | | // enable caching of mail data |
| | | $storage_cache = $this->config->get("{$driver}_cache"); |
| | | $messages_cache = $this->config->get('messages_cache'); |
| | | // for backward compatybility |
| | | if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { |
| | | $storage_cache = 'db'; |
| | | $messages_cache = true; |
| | | } |
| | | |
| | | if ($storage_cache) { |
| | | $this->storage->set_caching($storage_cache); |
| | | } |
| | | if ($messages_cache) { |
| | | $this->storage->set_messages_caching(true); |
| | | } |
| | | |
| | | // set pagesize from config |
| | | $pagesize = $this->config->get('mail_pagesize'); |
| | | if (!$pagesize) { |
| | | $pagesize = $this->config->get('pagesize', 50); |
| | | } |
| | | $this->storage->set_pagesize($pagesize); |
| | | |
| | | // set class options |
| | | $options = array( |
| | | 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), |
| | | 'auth_cid' => $this->config->get("{$driver}_auth_cid"), |
| | | 'auth_pw' => $this->config->get("{$driver}_auth_pw"), |
| | | 'debug' => (bool) $this->config->get("{$driver}_debug"), |
| | | 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), |
| | | 'timeout' => (int) $this->config->get("{$driver}_timeout"), |
| | | 'skip_deleted' => (bool) $this->config->get('skip_deleted'), |
| | | 'driver' => $driver, |
| | | ); |
| | | |
| | | if (!empty($_SESSION['storage_host'])) { |
| | | $options['host'] = $_SESSION['storage_host']; |
| | | $options['user'] = $_SESSION['username']; |
| | | $options['port'] = $_SESSION['storage_port']; |
| | | $options['ssl'] = $_SESSION['storage_ssl']; |
| | | $options['password'] = $this->decrypt($_SESSION['password']); |
| | | $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; |
| | | } |
| | | |
| | | $options = $this->plugins->exec_hook("storage_init", $options); |
| | | |
| | | // for backward compat. (deprecated, to be removed) |
| | | $options = $this->plugins->exec_hook("imap_init", $options); |
| | | |
| | | $this->storage->set_options($options); |
| | | $this->set_storage_prop(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set storage parameters. |
| | | * This must be done AFTER connecting to the server! |
| | | */ |
| | | protected function set_storage_prop() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); |
| | | |
| | | if ($default_folders = $this->config->get('default_folders')) { |
| | | $storage->set_default_folders($default_folders); |
| | | } |
| | | if (isset($_SESSION['mbox'])) { |
| | | $storage->set_folder($_SESSION['mbox']); |
| | | } |
| | | if (isset($_SESSION['page'])) { |
| | | $storage->set_page($_SESSION['page']); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | |
| | | $sess_name = $this->config->get('session_name'); |
| | | $sess_domain = $this->config->get('session_domain'); |
| | | $sess_path = $this->config->get('session_path'); |
| | | $lifetime = $this->config->get('session_lifetime', 0) * 60; |
| | | |
| | | // set session domain |
| | | if ($sess_domain) { |
| | | ini_set('session.cookie_domain', $sess_domain); |
| | | } |
| | | // set session path |
| | | if ($sess_path) { |
| | | ini_set('session.cookie_path', $sess_path); |
| | | } |
| | | // set session garbage collecting time according to session_lifetime |
| | | if ($lifetime) { |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get localized text in the desired language |
| | | * |
| | | * @param mixed $attrib Named parameters array or label name |
| | | * @param string $domain Label domain (plugin) name |
| | | * |
| | | * @return string Localized text |
| | | */ |
| | | public function gettext($attrib, $domain=null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | // extract attributes |
| | | if (is_string($attrib)) |
| | | $attrib = array('name' => $attrib); |
| | | |
| | | $name = $attrib['name'] ? $attrib['name'] : ''; |
| | | |
| | | // attrib contain text values: use them from now |
| | | if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) |
| | | $this->texts[$name] = $setval; |
| | | |
| | | // check for text with domain |
| | | if ($domain && ($text = $this->texts[$domain.'.'.$name])) |
| | | ; |
| | | // text does not exist |
| | | else if (!($text = $this->texts[$name])) { |
| | | return "[$name]"; |
| | | } |
| | | |
| | | // replace vars in text |
| | | if (is_array($attrib['vars'])) { |
| | | foreach ($attrib['vars'] as $var_key => $var_value) |
| | | $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); |
| | | } |
| | | |
| | | // format output |
| | | if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst']) |
| | | return ucfirst($text); |
| | | else if ($attrib['uppercase']) |
| | | return mb_strtoupper($text); |
| | | else if ($attrib['lowercase']) |
| | | return mb_strtolower($text); |
| | | |
| | | return strtr($text, array('\n' => "\n")); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if the given text label exists |
| | | * |
| | | * @param string $name Label name |
| | | * @param string $domain Label domain (plugin) name or '*' for all domains |
| | | * @param string $ref_domain Sets domain name if label is found |
| | | * |
| | | * @return boolean True if text exists (either in the current language or in en_US) |
| | | */ |
| | | public function text_exists($name, $domain = null, &$ref_domain = null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | if (isset($this->texts[$name])) { |
| | | $ref_domain = ''; |
| | | return true; |
| | | } |
| | | |
| | | // any of loaded domains (plugins) |
| | | if ($domain == '*') { |
| | | foreach ($this->plugins->loaded_plugins() as $domain) |
| | | if (isset($this->texts[$domain.'.'.$name])) { |
| | | $ref_domain = $domain; |
| | | return true; |
| | | } |
| | | } |
| | | // specified domain |
| | | else if ($domain) { |
| | | $ref_domain = $domain; |
| | | return isset($this->texts[$domain.'.'.$name]); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Load a localization package |
| | | * |
| | | * @param string Language ID |
| | | */ |
| | | public function load_language($lang = null, $add = array()) |
| | | { |
| | | $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); |
| | | |
| | | // load localized texts |
| | | if (empty($this->texts) || $lang != $_SESSION['language']) { |
| | | $this->texts = array(); |
| | | |
| | | // handle empty lines after closing PHP tag in localization files |
| | | ob_start(); |
| | | |
| | | // get english labels (these should be complete) |
| | | @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); |
| | | @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = $labels; |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | |
| | | // include user language files |
| | | if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = array_merge($this->texts, $labels); |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | } |
| | | |
| | | ob_end_clean(); |
| | | |
| | | $_SESSION['language'] = $lang; |
| | | } |
| | | |
| | | // append additional texts (from plugin) |
| | | if (is_array($add) && !empty($add)) |
| | | $this->texts += $add; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check the given string and return a valid language code |
| | | * |
| | | * @param string Language code |
| | | * @return string Valid language code |
| | | */ |
| | | protected function language_prop($lang) |
| | | { |
| | | static $rcube_languages, $rcube_language_aliases; |
| | | |
| | | // user HTTP_ACCEPT_LANGUAGE if no language is specified |
| | | if (empty($lang) || $lang == 'auto') { |
| | | $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
| | | $lang = str_replace('-', '_', $accept_langs[0]); |
| | | } |
| | | |
| | | if (empty($rcube_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | } |
| | | |
| | | // check if we have an alias for that language |
| | | if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { |
| | | $lang = $rcube_language_aliases[$lang]; |
| | | } |
| | | // try the first two chars |
| | | else if (!isset($rcube_languages[$lang])) { |
| | | $short = substr($lang, 0, 2); |
| | | |
| | | // check if we have an alias for the short language code |
| | | if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { |
| | | $lang = $rcube_language_aliases[$short]; |
| | | } |
| | | // expand 'nn' to 'nn_NN' |
| | | else if (!isset($rcube_languages[$short])) { |
| | | $lang = $short.'_'.strtoupper($short); |
| | | } |
| | | } |
| | | |
| | | if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | $lang = 'en_US'; |
| | | } |
| | | |
| | | return $lang; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read directory program/localization and return a list of available languages |
| | | * |
| | | * @return array List of available localizations |
| | | */ |
| | | public function list_languages() |
| | | { |
| | | static $sa_languages = array(); |
| | | |
| | | if (!sizeof($sa_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | |
| | | if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { |
| | | while (($name = readdir($dh)) !== false) { |
| | | if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) |
| | | continue; |
| | | |
| | | if ($label = $rcube_languages[$name]) |
| | | $sa_languages[$name] = $label; |
| | | } |
| | | closedir($dh); |
| | | } |
| | | } |
| | | |
| | | return $sa_languages; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypt using 3DES |
| | | * |
| | | * @param string $clear clear text input |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not to base64_encode() the result before returning |
| | | * |
| | | * @return string encrypted text |
| | | */ |
| | | public function encrypt($clear, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$clear) |
| | | return ''; |
| | | |
| | | /*- |
| | | * Add a single canary byte to the end of the clear text, which |
| | | * will help find out how much of padding will need to be removed |
| | | * upon decryption; see http://php.net/mcrypt_generic#68082 |
| | | /** |
| | | * Get localized text in the desired language |
| | | * |
| | | * @param mixed $attrib Named parameters array or label name |
| | | * @param string $domain Label domain (plugin) name |
| | | * |
| | | * @return string Localized text |
| | | */ |
| | | $clear = pack("a*H2", $clear, "80"); |
| | | public function gettext($attrib, $domain=null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) { |
| | | $this->load_language(); |
| | | } |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { |
| | | $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $cipher = $iv . mcrypt_generic($td, $clear); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | // extract attributes |
| | | if (is_string($attrib)) { |
| | | $attrib = array('name' => $attrib); |
| | | } |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = $this->create_iv($des_iv_size); |
| | | $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | $name = $attrib['name'] ? $attrib['name'] : ''; |
| | | |
| | | // attrib contain text values: use them from now |
| | | if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) { |
| | | $this->texts[$name] = $setval; |
| | | } |
| | | |
| | | // check for text with domain |
| | | if ($domain && ($text = $this->texts[$domain.'.'.$name])) { |
| | | } |
| | | // text does not exist |
| | | else if (!($text = $this->texts[$name])) { |
| | | return "[$name]"; |
| | | } |
| | | |
| | | // replace vars in text |
| | | if (is_array($attrib['vars'])) { |
| | | foreach ($attrib['vars'] as $var_key => $var_value) { |
| | | $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); |
| | | } |
| | | } |
| | | |
| | | // format output |
| | | if (($attrib['uppercase'] && strtolower($attrib['uppercase'] == 'first')) || $attrib['ucfirst']) { |
| | | return ucfirst($text); |
| | | } |
| | | else if ($attrib['uppercase']) { |
| | | return mb_strtoupper($text); |
| | | } |
| | | else if ($attrib['lowercase']) { |
| | | return mb_strtolower($text); |
| | | } |
| | | |
| | | return strtr($text, array('\n' => "\n")); |
| | | } |
| | | |
| | | return $base64 ? base64_encode($cipher) : $cipher; |
| | | } |
| | | |
| | | /** |
| | | * Decrypt 3DES-encrypted string |
| | | * |
| | | * @param string $cipher encrypted text |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not input is base64-encoded |
| | | * |
| | | * @return string decrypted text |
| | | */ |
| | | public function decrypt($cipher, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$cipher) |
| | | return ''; |
| | | /** |
| | | * Check if the given text label exists |
| | | * |
| | | * @param string $name Label name |
| | | * @param string $domain Label domain (plugin) name or '*' for all domains |
| | | * @param string $ref_domain Sets domain name if label is found |
| | | * |
| | | * @return boolean True if text exists (either in the current language or in en_US) |
| | | */ |
| | | public function text_exists($name, $domain = null, &$ref_domain = null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) { |
| | | $this->load_language(); |
| | | } |
| | | |
| | | $cipher = $base64 ? base64_decode($cipher) : $cipher; |
| | | if (isset($this->texts[$name])) { |
| | | $ref_domain = ''; |
| | | return true; |
| | | } |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { |
| | | $iv_size = mcrypt_enc_get_iv_size($td); |
| | | $iv = substr($cipher, 0, $iv_size); |
| | | // any of loaded domains (plugins) |
| | | if ($domain == '*') { |
| | | foreach ($this->plugins->loaded_plugins() as $domain) { |
| | | if (isset($this->texts[$domain.'.'.$name])) { |
| | | $ref_domain = $domain; |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | // specified domain |
| | | else if ($domain) { |
| | | $ref_domain = $domain; |
| | | return isset($this->texts[$domain.'.'.$name]); |
| | | } |
| | | |
| | | // session corruption? (#1485970) |
| | | if (strlen($iv) < $iv_size) |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Load a localization package |
| | | * |
| | | * @param string Language ID |
| | | * @param array Additional text labels/messages |
| | | */ |
| | | public function load_language($lang = null, $add = array()) |
| | | { |
| | | $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); |
| | | |
| | | // load localized texts |
| | | if (empty($this->texts) || $lang != $_SESSION['language']) { |
| | | $this->texts = array(); |
| | | |
| | | // handle empty lines after closing PHP tag in localization files |
| | | ob_start(); |
| | | |
| | | // get english labels (these should be complete) |
| | | @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); |
| | | @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = $labels; |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | |
| | | // include user language files |
| | | if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = array_merge($this->texts, $labels); |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | } |
| | | |
| | | ob_end_clean(); |
| | | |
| | | $_SESSION['language'] = $lang; |
| | | } |
| | | |
| | | // append additional texts (from plugin) |
| | | if (is_array($add) && !empty($add)) { |
| | | $this->texts += $add; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check the given string and return a valid language code |
| | | * |
| | | * @param string Language code |
| | | * |
| | | * @return string Valid language code |
| | | */ |
| | | protected function language_prop($lang) |
| | | { |
| | | static $rcube_languages, $rcube_language_aliases; |
| | | |
| | | // user HTTP_ACCEPT_LANGUAGE if no language is specified |
| | | if (empty($lang) || $lang == 'auto') { |
| | | $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
| | | $lang = str_replace('-', '_', $accept_langs[0]); |
| | | } |
| | | |
| | | if (empty($rcube_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | } |
| | | |
| | | // check if we have an alias for that language |
| | | if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { |
| | | $lang = $rcube_language_aliases[$lang]; |
| | | } |
| | | // try the first two chars |
| | | else if (!isset($rcube_languages[$lang])) { |
| | | $short = substr($lang, 0, 2); |
| | | |
| | | // check if we have an alias for the short language code |
| | | if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { |
| | | $lang = $rcube_language_aliases[$short]; |
| | | } |
| | | // expand 'nn' to 'nn_NN' |
| | | else if (!isset($rcube_languages[$short])) { |
| | | $lang = $short.'_'.strtoupper($short); |
| | | } |
| | | } |
| | | |
| | | if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | $lang = 'en_US'; |
| | | } |
| | | |
| | | return $lang; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read directory program/localization and return a list of available languages |
| | | * |
| | | * @return array List of available localizations |
| | | */ |
| | | public function list_languages() |
| | | { |
| | | static $sa_languages = array(); |
| | | |
| | | if (!sizeof($sa_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | |
| | | if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { |
| | | while (($name = readdir($dh)) !== false) { |
| | | if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) { |
| | | continue; |
| | | } |
| | | |
| | | if ($label = $rcube_languages[$name]) { |
| | | $sa_languages[$name] = $label; |
| | | } |
| | | } |
| | | closedir($dh); |
| | | } |
| | | } |
| | | |
| | | return $sa_languages; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypt using 3DES |
| | | * |
| | | * @param string $clear clear text input |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not to base64_encode() the result before returning |
| | | * |
| | | * @return string encrypted text |
| | | */ |
| | | public function encrypt($clear, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$clear) { |
| | | return ''; |
| | | } |
| | | |
| | | /*- |
| | | * Add a single canary byte to the end of the clear text, which |
| | | * will help find out how much of padding will need to be removed |
| | | * upon decryption; see http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = pack("a*H2", $clear, "80"); |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) |
| | | ) { |
| | | $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $cipher = $iv . mcrypt_generic($td, $clear); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = $this->create_iv($des_iv_size); |
| | | $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | return $base64 ? base64_encode($cipher) : $cipher; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decrypt 3DES-encrypted string |
| | | * |
| | | * @param string $cipher encrypted text |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not input is base64-encoded |
| | | * |
| | | * @return string decrypted text |
| | | */ |
| | | public function decrypt($cipher, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$cipher) { |
| | | return ''; |
| | | } |
| | | |
| | | $cipher = $base64 ? base64_decode($cipher) : $cipher; |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) |
| | | ) { |
| | | $iv_size = mcrypt_enc_get_iv_size($td); |
| | | $iv = substr($cipher, 0, $iv_size); |
| | | |
| | | // session corruption? (#1485970) |
| | | if (strlen($iv) < $iv_size) { |
| | | return ''; |
| | | } |
| | | |
| | | $cipher = substr($cipher, $iv_size); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $clear = mdecrypt_generic($td, $cipher); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = substr($cipher, 0, $des_iv_size); |
| | | $cipher = substr($cipher, $des_iv_size); |
| | | $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /*- |
| | | * Trim PHP's padding and the canary byte; see note in |
| | | * rcube::encrypt() and http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = substr(rtrim($clear, "\0"), 0, -1); |
| | | |
| | | return $clear; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generates encryption initialization vector (IV) |
| | | * |
| | | * @param int Vector size |
| | | * |
| | | * @return string Vector string |
| | | */ |
| | | private function create_iv($size) |
| | | { |
| | | // mcrypt_create_iv() can be slow when system lacks entrophy |
| | | // we'll generate IV vector manually |
| | | $iv = ''; |
| | | for ($i = 0; $i < $size; $i++) { |
| | | $iv .= chr(mt_rand(0, 255)); |
| | | } |
| | | |
| | | return $iv; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Build a valid URL to this instance of Roundcube |
| | | * |
| | | * @param mixed Either a string with the action or url parameters as key-value pairs |
| | | * @return string Valid application URL |
| | | */ |
| | | public function url($p) |
| | | { |
| | | // STUB: should be overloaded by the application |
| | | return ''; |
| | | |
| | | $cipher = substr($cipher, $iv_size); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $clear = mdecrypt_generic($td, $cipher); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = substr($cipher, 0, $des_iv_size); |
| | | $cipher = substr($cipher, $des_iv_size); |
| | | $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /*- |
| | | * Trim PHP's padding and the canary byte; see note in |
| | | * rcube::encrypt() and http://php.net/mcrypt_generic#68082 |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | * Registered with register_shutdown_function() |
| | | */ |
| | | $clear = substr(rtrim($clear, "\0"), 0, -1); |
| | | |
| | | return $clear; |
| | | } |
| | | |
| | | /** |
| | | * Generates encryption initialization vector (IV) |
| | | * |
| | | * @param int Vector size |
| | | * @return string Vector string |
| | | */ |
| | | private function create_iv($size) |
| | | { |
| | | // mcrypt_create_iv() can be slow when system lacks entrophy |
| | | // we'll generate IV vector manually |
| | | $iv = ''; |
| | | for ($i = 0; $i < $size; $i++) |
| | | $iv .= chr(mt_rand(0, 255)); |
| | | return $iv; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Build a valid URL to this instance of Roundcube |
| | | * |
| | | * @param mixed Either a string with the action or url parameters as key-value pairs |
| | | * @return string Valid application URL |
| | | */ |
| | | public function url($p) |
| | | { |
| | | // STUB: should be overloaded by the application |
| | | return ''; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | * Registered with register_shutdown_function() |
| | | */ |
| | | public function shutdown() |
| | | { |
| | | foreach ($this->shutdown_functions as $function) |
| | | call_user_func($function); |
| | | |
| | | if (is_object($this->smtp)) |
| | | $this->smtp->disconnect(); |
| | | |
| | | foreach ($this->caches as $cache) { |
| | | if (is_object($cache)) |
| | | $cache->close(); |
| | | } |
| | | |
| | | if (is_object($this->storage)) { |
| | | if ($this->expunge_cache) |
| | | $this->storage->expunge_cache(); |
| | | $this->storage->close(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers shutdown function to be executed on shutdown. |
| | | * The functions will be executed before destroying any |
| | | * objects like smtp, imap, session, etc. |
| | | * |
| | | * @param callback Function callback |
| | | */ |
| | | public function add_shutdown_function($function) |
| | | { |
| | | $this->shutdown_functions[] = $function; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Construct shell command, execute it and return output as string. |
| | | * Keywords {keyword} are replaced with arguments |
| | | * |
| | | * @param $cmd Format string with {keywords} to be replaced |
| | | * @param $values (zero, one or more arrays can be passed) |
| | | * @return output of command. shell errors not detectable |
| | | */ |
| | | public static function exec(/* $cmd, $values1 = array(), ... */) |
| | | { |
| | | $args = func_get_args(); |
| | | $cmd = array_shift($args); |
| | | $values = $replacements = array(); |
| | | |
| | | // merge values into one array |
| | | foreach ($args as $arg) |
| | | $values += (array)$arg; |
| | | |
| | | preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); |
| | | foreach ($matches as $tags) { |
| | | list(, $tag, $option, $key) = $tags; |
| | | $parts = array(); |
| | | |
| | | if ($option) { |
| | | foreach ((array)$values["-$key"] as $key => $value) { |
| | | if ($value === true || $value === false || $value === null) |
| | | $parts[] = $value ? $key : ""; |
| | | else foreach ((array)$value as $val) |
| | | $parts[] = "$key " . escapeshellarg($val); |
| | | public function shutdown() |
| | | { |
| | | foreach ($this->shutdown_functions as $function) { |
| | | call_user_func($function); |
| | | } |
| | | } |
| | | else { |
| | | foreach ((array)$values[$key] as $value) |
| | | $parts[] = escapeshellarg($value); |
| | | } |
| | | |
| | | $replacements[$tag] = join(" ", $parts); |
| | | if (is_object($this->smtp)) { |
| | | $this->smtp->disconnect(); |
| | | } |
| | | |
| | | foreach ($this->caches as $cache) { |
| | | if (is_object($cache)) { |
| | | $cache->close(); |
| | | } |
| | | } |
| | | |
| | | if (is_object($this->storage)) { |
| | | if ($this->expunge_cache) { |
| | | $this->storage->expunge_cache(); |
| | | } |
| | | $this->storage->close(); |
| | | } |
| | | } |
| | | |
| | | // use strtr behaviour of going through source string once |
| | | $cmd = strtr($cmd, $replacements); |
| | | |
| | | return (string)shell_exec($cmd); |
| | | } |
| | | /** |
| | | * Registers shutdown function to be executed on shutdown. |
| | | * The functions will be executed before destroying any |
| | | * objects like smtp, imap, session, etc. |
| | | * |
| | | * @param callback Function callback |
| | | */ |
| | | public function add_shutdown_function($function) |
| | | { |
| | | $this->shutdown_functions[] = $function; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Construct shell command, execute it and return output as string. |
| | | * Keywords {keyword} are replaced with arguments |
| | | * |
| | | * @param $cmd Format string with {keywords} to be replaced |
| | | * @param $values (zero, one or more arrays can be passed) |
| | | * |
| | | * @return output of command. shell errors not detectable |
| | | */ |
| | | public static function exec(/* $cmd, $values1 = array(), ... */) |
| | | { |
| | | $args = func_get_args(); |
| | | $cmd = array_shift($args); |
| | | $values = $replacements = array(); |
| | | |
| | | // merge values into one array |
| | | foreach ($args as $arg) { |
| | | $values += (array)$arg; |
| | | } |
| | | |
| | | preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); |
| | | foreach ($matches as $tags) { |
| | | list(, $tag, $option, $key) = $tags; |
| | | $parts = array(); |
| | | |
| | | if ($option) { |
| | | foreach ((array)$values["-$key"] as $key => $value) { |
| | | if ($value === true || $value === false || $value === null) { |
| | | $parts[] = $value ? $key : ""; |
| | | } |
| | | else { |
| | | foreach ((array)$value as $val) { |
| | | $parts[] = "$key " . escapeshellarg($val); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else { |
| | | foreach ((array)$values[$key] as $value) { |
| | | $parts[] = escapeshellarg($value); |
| | | } |
| | | } |
| | | |
| | | $replacements[$tag] = join(" ", $parts); |
| | | } |
| | | |
| | | // use strtr behaviour of going through source string once |
| | | $cmd = strtr($cmd, $replacements); |
| | | |
| | | return (string)shell_exec($cmd); |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | return rcmail::get_instance()->db->table_name($table); |
| | | } |
| | | |
| | | function get_sequence_name($sequence) |
| | | { |
| | | return rcmail::get_instance()->db->sequence_name($sequence); |
| | | } |
| | | |
| | | function rcube_label($p, $domain=null) |
| | | { |
| | | return rcmail::get_instance()->gettext($p, $domain); |
| | |
| | | */ |
| | | |
| | | /** |
| | | * rcube_browser |
| | | * |
| | | * Provide details about the client's browser based on the User-Agent header |
| | | * |
| | | * @package Core |
| | |
| | | } |
| | | else if ($this->type == 'apc') { |
| | | $data = apc_fetch($this->ckey($key)); |
| | | } |
| | | } |
| | | |
| | | if ($data) { |
| | | $md5sum = md5($data); |
| | |
| | | } |
| | | |
| | | $this->cache[$key] = $data; |
| | | $this->cache_sums[$key] = $md5sum; |
| | | $this->cache_sums[$key] = $md5sum; |
| | | } |
| | | else { |
| | | $this->cache[$key] = null; |
| | |
| | | $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse_charset($to); |
| | | $from = self::parse_charset($from); |
| | | |
| | | // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654) |
| | | // In that case we can just skip the conversion (use UTF-8) |
| | | if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) { |
| | | $from = 'UTF-8'; |
| | | } |
| | | |
| | | if ($from == $to || empty($str) || empty($from)) { |
| | | return $str; |
| | | } |
| | |
| | | 'default_folders' => 'default_imap_folders', |
| | | 'mail_pagesize' => 'pagesize', |
| | | 'addressbook_pagesize' => 'pagesize', |
| | | 'reply_mode' => 'top_posting', |
| | | ); |
| | | |
| | | |
| | |
| | | if (strlen($key) != 24) { |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Configured crypto key '$key' is not exactly 24 bytes long" |
| | | ), true, true); |
| | | } |
| | |
| | | else |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Invalid mail_header_delimiter setting" |
| | | ), true, false); |
| | | } |
| | |
| | | return intval($input); |
| | | } |
| | | |
| | | if (is_null($input)) { |
| | | return 'NULL'; |
| | | } |
| | | |
| | | // create DB handle if not available |
| | | if (!$this->dbh) { |
| | | $this->db_connect('r'); |
| | |
| | | |
| | | return array( |
| | | $this->search_string, |
| | | $this->search_set, |
| | | $this->search_charset, |
| | | $this->search_sort_field, |
| | | $this->search_sorted, |
| | | ); |
| | | $this->search_set, |
| | | $this->search_charset, |
| | | $this->search_sort_field, |
| | | $this->search_sorted, |
| | | ); |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | /** |
| | | * Sends the whole message source to stdout |
| | | * |
| | | * @param int $uid Message UID |
| | | * @param bool $formatted Enables line-ending formatting |
| | | */ |
| | | public function print_raw_body($uid) |
| | | public function print_raw_body($uid, $formatted = true) |
| | | { |
| | | if (!$this->check_connection()) { |
| | | return; |
| | | } |
| | | |
| | | $this->conn->handlePartBody($this->folder, $uid, true, NULL, NULL, true); |
| | | $this->conn->handlePartBody($this->folder, $uid, true, null, null, true, null, $formatted); |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | if (!strlen($folder)) { |
| | | $folder = $this->folder; |
| | | } |
| | | |
| | | if (!$this->check_connection()) { |
| | | return false; |
| | | } |
| | | |
| | | // make sure folder exists |
| | |
| | | protected function rsort($folder, $delimiter, &$list, &$out) |
| | | { |
| | | while (list($key, $name) = each($list)) { |
| | | if (strpos($name, $folder.$delimiter) === 0) { |
| | | // set the type of folder name variable (#1485527) |
| | | $out[] = (string) $name; |
| | | unset($list[$key]); |
| | | $this->rsort($name, $delimiter, $list, $out); |
| | | } |
| | | if (strpos($name, $folder.$delimiter) === 0) { |
| | | // set the type of folder name variable (#1485527) |
| | | $out[] = (string) $name; |
| | | unset($list[$key]); |
| | | $this->rsort($name, $delimiter, $list, $out); |
| | | } |
| | | } |
| | | reset($list); |
| | | } |
| | |
| | | | program/include/rcube_imap_cache.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2011, 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. | |
| | |
| | | function get_message($mailbox, $uid, $update = true, $cache = true) |
| | | { |
| | | // Check internal cache |
| | | if ($this->icache['message'] |
| | | && $this->icache['message']['mailbox'] == $mailbox |
| | | && $this->icache['message']['object']->uid == $uid |
| | | if ($this->icache['__message'] |
| | | && $this->icache['__message']['mailbox'] == $mailbox |
| | | && $this->icache['__message']['object']->uid == $uid |
| | | ) { |
| | | return $this->icache['message']['object']; |
| | | return $this->icache['__message']['object']; |
| | | } |
| | | |
| | | $sql_result = $this->db->query( |
| | |
| | | // Save current message from internal cache |
| | | $this->save_icache(); |
| | | |
| | | $this->icache['message'] = array( |
| | | $this->icache['__message'] = array( |
| | | 'object' => $message, |
| | | 'mailbox' => $mailbox, |
| | | 'exists' => $found, |
| | |
| | | */ |
| | | function change_flag($mailbox, $uids, $flag, $enabled = false) |
| | | { |
| | | if (empty($uids)) { |
| | | return; |
| | | } |
| | | |
| | | $flag = strtoupper($flag); |
| | | $idx = (int) array_search($flag, $this->flags); |
| | | $uids = (array) $uids; |
| | | |
| | | if (!$idx) { |
| | | return; |
| | | } |
| | | |
| | | // Internal cache update |
| | | if ($uids && count($uids) == 1 && ($uid = current($uids)) |
| | | && ($message = $this->icache['message']) |
| | | && $message['mailbox'] == $mailbox && $message['object']->uid == $uid |
| | | if (($message = $this->icache['__message']) |
| | | && $message['mailbox'] === $mailbox |
| | | && in_array($message['object']->uid, $uids) |
| | | ) { |
| | | $message['object']->flags[$flag] = $enabled; |
| | | return; |
| | | |
| | | if (count($uids) == 1) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | $this->db->query( |
| | |
| | | .", flags = flags ".($enabled ? "+ $idx" : "- $idx") |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?" |
| | | .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : "") |
| | | .($uids !== null ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "") |
| | | ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"), |
| | | $this->userid, $mailbox); |
| | | } |
| | |
| | | } |
| | | else { |
| | | // Remove the message from internal cache |
| | | if (!empty($uids) && !is_array($uids) && ($message = $this->icache['message']) |
| | | && $message['mailbox'] == $mailbox && $message['object']->uid == $uids |
| | | if (!empty($uids) && ($message = $this->icache['__message']) |
| | | && $message['mailbox'] === $mailbox |
| | | && in_array($message['object']->uid, (array)$uids) |
| | | ) { |
| | | $this->icache['message'] = null; |
| | | $this->icache['__message'] = null; |
| | | } |
| | | |
| | | $this->db->query( |
| | |
| | | // get expiration timestamp |
| | | $ts = get_offset_time($ttl, -1); |
| | | |
| | | $this->db->query("DELETE FROM ".get_table_name('cache_messages') |
| | | $this->db->query("DELETE FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE changed < " . $this->db->fromunixtime($ts)); |
| | | |
| | | $this->db->query("DELETE FROM ".get_table_name('cache_index') |
| | | $this->db->query("DELETE FROM ".$this->db->table_name('cache_index') |
| | | ." WHERE changed < " . $this->db->fromunixtime($ts)); |
| | | |
| | | $this->db->query("DELETE FROM ".get_table_name('cache_thread') |
| | | $this->db->query("DELETE FROM ".$this->db->table_name('cache_thread') |
| | | ." WHERE changed < " . $this->db->fromunixtime($ts)); |
| | | } |
| | | |
| | |
| | | { |
| | | $object = $index['object']; |
| | | $is_thread = is_a($object, 'rcube_result_thread'); |
| | | |
| | | // sanity check |
| | | if (empty($object)) { |
| | | return false; |
| | | } |
| | | |
| | | // Get mailbox data (UIDVALIDITY, counters, etc.) for status check |
| | | $mbox_data = $this->imap->folder_data($mailbox); |
| | |
| | | private function save_icache() |
| | | { |
| | | // Save current message from internal cache |
| | | if ($message = $this->icache['message']) { |
| | | if ($message = $this->icache['__message']) { |
| | | // clean up some object's data |
| | | $object = $this->message_object_prepare($message['object']); |
| | | |
| | |
| | | $this->add_message($message['mailbox'], $object, !$message['exists']); |
| | | } |
| | | |
| | | $this->icache['message']['md5sum'] = $md5sum; |
| | | $this->icache['__message']['md5sum'] = $md5sum; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | 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); |
| | |
| | | return $this->handlePartBody($mailbox, $id, $is_uid, $part); |
| | | } |
| | | |
| | | function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL) |
| | | function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL, $formatted=true) |
| | | { |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | |
| | | continue; |
| | | $line = convert_uudecode($line); |
| | | // default |
| | | } else { |
| | | } else if ($formatted) { |
| | | $line = rtrim($line, "\t\r\n\0\x0B") . "\n"; |
| | | } |
| | | |
| | |
| | | { |
| | | unset($this->data['APPENDUID']); |
| | | |
| | | if (!$mailbox) { |
| | | if ($mailbox === null || $mailbox === '') { |
| | | return false; |
| | | } |
| | | |
| | |
| | | { |
| | | unset($this->data['APPENDUID']); |
| | | |
| | | if (!$mailbox) { |
| | | if ($mailbox === null || $mailbox === '') { |
| | | return false; |
| | | } |
| | | |
| | |
| | | if (file_exists(realpath($path))) { |
| | | $in_fp = fopen($path, 'r'); |
| | | } |
| | | |
| | | if (!$in_fp) { |
| | | $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); |
| | | return false; |
| | |
| | | unset($this->coltypes[$childcol]); // remove address child col from global coltypes list |
| | | } |
| | | } |
| | | |
| | | // at least one address type must be specified |
| | | if (empty($this->coltypes['address']['subtypes'])) { |
| | | $this->coltypes['address']['subtypes'] = array('home'); |
| | | } |
| | | } |
| | | else if ($this->coltypes['address']) { |
| | | $this->coltypes['address'] += array('type' => 'textarea', 'childs' => null, 'size' => 40); |
| | |
| | | } |
| | | |
| | | // use VLV pseudo-search for autocompletion |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcube = rcube::get_instance(); |
| | | |
| | | if ($this->prop['vlv_search'] && $this->conn && join(',', (array)$fields) == join(',', $rcmail->config->get('contactlist_fields'))) |
| | | if ($this->prop['vlv_search'] && $this->conn && join(',', (array)$fields) == join(',', $rcube->config->get('contactlist_fields'))) |
| | | { |
| | | // add general filter to query |
| | | if (!empty($this->prop['filter']) && empty($this->filter)) |
| | |
| | | # a0 = type context-specific/constructed with a length of 06 (6) bytes following |
| | | # 02 = type integer with 2 bytes following (offset): 01 01 (ie 1) |
| | | # 02 = type integer with 2 bytes following (contentCount): 01 00 |
| | | |
| | | |
| | | # whith a search string present: |
| | | # 81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here) |
| | | # 81 indicates a user string is present where as a a0 indicates just a offset search |
| | | # 81 = type context-specific/constructed with a length of 06 (6) bytes following |
| | | |
| | | |
| | | # the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the |
| | | # encoding of integer values (note: these values are in |
| | | # two-complement form so since offset will never be negative bit 8 of the |
| | |
| | | # of the second (to the left of first octet) octet: |
| | | # a) shall not all be ones; and |
| | | # b) shall not all be zero |
| | | |
| | | |
| | | if ($search) |
| | | { |
| | | $search = preg_replace('/[^-[:alpha:] ,.()0-9]+/', '', $search); |
| | |
| | | // now compute length over $str |
| | | $str = self::_ber_addseq($str, 'a0'); |
| | | } |
| | | |
| | | |
| | | // now tack on records per page |
| | | $str = "020100" . self::_ber_addseq(self::_ber_encode_int($rpp-1), '02') . $str; |
| | | |
| | |
| | | */ |
| | | private $mime; |
| | | private $opt = array(); |
| | | private $inline_parts = array(); |
| | | private $parse_alternative = false; |
| | | |
| | | public $uid = null; |
| | | public $uid; |
| | | public $folder; |
| | | public $headers; |
| | | public $parts = array(); |
| | | public $mime_parts = array(); |
| | | public $inline_parts = array(); |
| | | public $attachments = array(); |
| | | public $subject = ''; |
| | | public $sender = null; |
| | |
| | | * |
| | | * Provide a uid, and parse message structure. |
| | | * |
| | | * @param string $uid The message UID. |
| | | * @param string $uid The message UID. |
| | | * @param string $folder Folder name |
| | | * |
| | | * @see self::$app, self::$storage, self::$opt, self::$parts |
| | | */ |
| | | function __construct($uid) |
| | | function __construct($uid, $folder = null) |
| | | { |
| | | $this->uid = $uid; |
| | | $this->app = rcube::get_instance(); |
| | | $this->storage = $this->app->get_storage(); |
| | | $this->folder = strlen($folder) ? $folder : $this->storage->get_folder(); |
| | | $this->storage->set_options(array('all_headers' => true)); |
| | | |
| | | // Set current folder |
| | | $this->storage->set_folder($this->folder); |
| | | |
| | | $this->headers = $this->storage->get_message($uid); |
| | | |
| | |
| | | } |
| | | return $fp ? true : $part->body; |
| | | } |
| | | |
| | | // get from IMAP |
| | | $this->storage->set_folder($this->folder); |
| | | |
| | | return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv); |
| | | } else |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | function tnef_decode(&$part) |
| | | { |
| | | // @TODO: attachment may be huge, hadle it via file |
| | | if (!isset($part->body)) |
| | | if (!isset($part->body)) { |
| | | $this->storage->set_folder($this->folder); |
| | | $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); |
| | | } |
| | | |
| | | $parts = array(); |
| | | $tnef = new tnef_decoder; |
| | |
| | | function uu_decode(&$part) |
| | | { |
| | | // @TODO: messages may be huge, hadle body via file |
| | | if (!isset($part->body)) |
| | | if (!isset($part->body)) { |
| | | $this->storage->set_folder($this->folder); |
| | | $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); |
| | | } |
| | | |
| | | $parts = array(); |
| | | // FIXME: line length is max.65? |
| | |
| | | $prefix = $regs[0]; |
| | | $level = strlen($prefix); |
| | | $line = rtrim(substr($line, $level)); |
| | | $line = $prefix . rc_wordwrap($line, $length - $level - 2, " \r\n$prefix "); |
| | | $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix "); |
| | | } |
| | | else if ($line) { |
| | | $line = rc_wordwrap(rtrim($line), $length - 2, " \r\n"); |
| | | $line = self::wordwrap(rtrim($line), $length - 2, " \r\n"); |
| | | // space-stuffing |
| | | $line = preg_replace('/(^|\r\n)(From| |>)/', '\\1 \\2', $line); |
| | | } |
| | |
| | | $this->set_env('task', $task); |
| | | $this->set_env('x_frame_options', $this->config->get('x_frame_options', 'sameorigin')); |
| | | |
| | | // add cookie info |
| | | $this->set_env('cookie_domain', ini_get('session.cookie_domain')); |
| | | $this->set_env('cookie_path', ini_get('session.cookie_path')); |
| | | $this->set_env('cookie_secure', ini_get('session.cookie_secure')); |
| | | |
| | | // load the correct skin (in case user-defined) |
| | | $skin = $this->config->get('skin'); |
| | | $this->set_skin($skin); |
| | |
| | | 'line' => __LINE__, |
| | | 'file' => __FILE__, |
| | | 'message' => 'Error loading template for '.$realname |
| | | ), true, true); |
| | | ), true, $write); |
| | | return false; |
| | | } |
| | | |
| | |
| | | if ($attrib['name'] || $attrib['command']) { |
| | | return $this->button($attrib); |
| | | } |
| | | break; |
| | | |
| | | // frame |
| | | case 'frame': |
| | | return $this->frame($attrib); |
| | | break; |
| | | |
| | | // show a label |
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns iframe object, registers some related env variables |
| | | * |
| | | * @param array $attrib HTML attributes |
| | | * |
| | | * @return string IFRAME element |
| | | */ |
| | | public function frame($attrib) |
| | | { |
| | | if (!$attrib['id']) { |
| | | $attrib['id'] = 'rcmframe'; |
| | | } |
| | | |
| | | if (!$attrib['name']) { |
| | | $attrib['name'] = $attrib['id']; |
| | | } |
| | | |
| | | $this->set_env('contentframe', $attrib['id']); |
| | | $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif'); |
| | | |
| | | return html::iframe($attrib); |
| | | } |
| | | |
| | | |
| | | /* ************* common functions delivering gui objects ************** */ |
| | | |
| | | |
| | |
| | | if (empty($url) && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING'])) |
| | | $url = $_SERVER['QUERY_STRING']; |
| | | |
| | | // Disable autocapitalization on iPad/iPhone (#1488609) |
| | | $attrib['autocapitalize'] = 'off'; |
| | | |
| | | // set atocomplete attribute |
| | | $user_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off'); |
| | | $host_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off'); |
| | |
| | | public function local_skin_path() |
| | | { |
| | | $rcmail = rcube::get_instance(); |
| | | foreach (array($rcmail->config->get('skin'),'default') as $skin) { |
| | | foreach (array($rcmail->config->get('skin'), 'larry') as $skin) { |
| | | $skin_path = 'skins/' . $skin; |
| | | if (is_dir(realpath(slashify($this->home) . $skin_path))) |
| | | break; |
| | |
| | | class rcube_plugin_api |
| | | { |
| | | static private $instance; |
| | | |
| | | |
| | | public $dir; |
| | | public $url = 'plugins/'; |
| | | public $task = ''; |
| | | public $output; |
| | | |
| | | |
| | | public $handlers = array(); |
| | | private $plugins = array(); |
| | | private $tasks = array(); |
| | |
| | | public function set_keep_alive($keep_alive) |
| | | { |
| | | $this->keep_alive = $keep_alive; |
| | | |
| | | |
| | | if ($this->lifetime < $keep_alive) |
| | | $this->set_lifetime($keep_alive + 30); |
| | | } |
| | |
| | | { |
| | | return $this->ip; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for cookie encryption secret |
| | | */ |
| | |
| | | { |
| | | $this->ip_check = $check; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Setter for the cookie name used for session cookie |
| | | */ |
| | |
| | | $result = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!$result) |
| | | $this->log("Session authentication failed for " . $this->key . "; invalid auth cookie sent; timeslot = " . date('Y-m-d H:i:s', $prev)); |
| | |
| | | } |
| | | |
| | | /** |
| | | * |
| | | * Writes debug information to the log |
| | | */ |
| | | function log($line) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Remove slash at the end of the string |
| | | * Remove slashes at the end of the string |
| | | */ |
| | | function unslashify($str) |
| | | { |
| | | return preg_replace('/\/$/', '', $str); |
| | | return preg_replace('/\/+$/', '', $str); |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | $keys = array(); |
| | | |
| | | if (!empty($array)) { |
| | | if (!empty($array) && is_array($array)) { |
| | | foreach ($array as $key => $child) { |
| | | $keys[] = $key; |
| | | foreach (array_keys_recursive($child) as $val) { |
| | |
| | | |
| | | |
| | | /** |
| | | * Remove single and double quotes from given string |
| | | * Remove single and double quotes from a given string |
| | | * |
| | | * @param string Input value |
| | | * |
| | |
| | | } |
| | | |
| | | return "$name <$email>"; |
| | | } |
| | | |
| | | return $email; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Format e-mail address |
| | | * |
| | | * @param string $email E-mail address |
| | | * |
| | | * @return string Formatted e-mail address |
| | | */ |
| | | function format_email($email) |
| | | { |
| | | $email = trim($email); |
| | | $parts = explode('@', $email); |
| | | $count = count($parts); |
| | | |
| | | if ($count > 1) { |
| | | $parts[$count-1] = mb_strtolower($parts[$count-1]); |
| | | |
| | | $email = implode('@', $parts); |
| | | } |
| | | |
| | | return $email; |
| | |
| | | { |
| | | $filename = preg_replace( |
| | | array( |
| | | '/MDB2_(.+)/', |
| | | '/Mail_(.+)/', |
| | | '/Net_(.+)/', |
| | | '/Auth_(.+)/', |
| | |
| | | '/^utf8$/', |
| | | ), |
| | | array( |
| | | 'Mail/\\1', |
| | | 'Mail/\\1', |
| | | 'Net/\\1', |
| | | 'Auth/\\1', |
| | |
| | | $lines[] = $key . ': ' . $value; |
| | | } |
| | | } |
| | | |
| | | |
| | | return array($from, join(SMTP_MIME_CRLF, $lines) . SMTP_MIME_CRLF); |
| | | } |
| | | |
| | |
| | | */ |
| | | public function set_folder($folder) |
| | | { |
| | | if ($this->folder == $folder) { |
| | | if ($this->folder === $folder) { |
| | | return; |
| | | } |
| | | |
| | |
| | | |
| | | /** |
| | | * Sends the whole message source to stdout |
| | | * |
| | | * @param int $uid Message UID |
| | | * @param bool $formatted Enables line-ending formatting |
| | | */ |
| | | abstract function print_raw_body($uid); |
| | | abstract function print_raw_body($uid, $formatted = true); |
| | | |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | $data = $rcube->plugins->exec_hook('user_create', |
| | | array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email, 'host'=>$host)); |
| | | array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email, 'host'=>$host)); |
| | | |
| | | // plugin aborted this operation |
| | | if ($data['abort']) |
| | |
| | | |
| | | // from PEAR::Validate |
| | | $regexp = '&^(?: |
| | | ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name |
| | | ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) |
| | | $&xi'; |
| | | ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name |
| | | ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) |
| | | $&xi'; |
| | | |
| | | if (!preg_match($regexp, $local_part)) { |
| | | return false; |
| | | } |
| | | |
| | | // Check domain part |
| | | if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) { |
| | | return true; // IP address |
| | | // Validate domain part |
| | | if (preg_match('/^\[((IPv6:[0-9a-f:.]+)|([0-9.]+))\]$/i', $domain_part, $matches)) { |
| | | return self::check_ip(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address |
| | | } |
| | | else { |
| | | // If not an IP address |
| | |
| | | if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // last domain part |
| | | if (preg_match('/[^a-zA-Z]/', array_pop($domain_array))) { |
| | | return false; |
| | | } |
| | | |
| | | $rcube = rcube::get_instance(); |
| | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Validates IPv4 or IPv6 address |
| | | * |
| | | * @param string $ip IP address in v4 or v6 format |
| | | * |
| | | * @return bool True if the address is valid |
| | | */ |
| | | public static function check_ip($ip) |
| | | { |
| | | // IPv6, but there's no build-in IPv6 support |
| | | if (strpos($ip, ':') !== false && !defined('AF_INET6')) { |
| | | $parts = explode(':', $domain_part); |
| | | $count = count($parts); |
| | | |
| | | if ($count > 8 || $count < 2) { |
| | | return false; |
| | | } |
| | | |
| | | foreach ($parts as $idx => $part) { |
| | | $length = strlen($part); |
| | | if (!$length) { |
| | | // there can be only one :: |
| | | if ($found_empty) { |
| | | return false; |
| | | } |
| | | $found_empty = true; |
| | | } |
| | | // last part can be an IPv4 address |
| | | else if ($idx == $count - 1) { |
| | | if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { |
| | | return @inet_pton($part) !== false; |
| | | } |
| | | } |
| | | else if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | return @inet_pton($ip) !== false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check whether the HTTP referer matches the current request |
| | | * |
| | |
| | | public static function check_referer() |
| | | { |
| | | $uri = parse_url($_SERVER['REQUEST_URI']); |
| | | $referer = parse_url(rcube_request_header('Referer')); |
| | | return $referer['host'] == rcube_request_header('Host') && $referer['path'] == $uri['path']; |
| | | $referer = parse_url(self::request_header('Referer')); |
| | | return $referer['host'] == self::request_header('Host') && $referer['path'] == $uri['path']; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | case 'birthday': |
| | | case 'anniversary': |
| | | if (($val = rcube_strtotime($value)) && ($fn = self::$fieldmap[$field])) |
| | | if (($val = rcube_utils::strtotime($value)) && ($fn = self::$fieldmap[$field])) |
| | | $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); |
| | | break; |
| | | |
| | |
| | | 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])) |
| | |
| | | // do reply-list, when list is detected and popup menu wasn't used |
| | | url._all = (!props && this.commands['reply-list'] ? 'list' : 'all'); |
| | | else if (command == 'reply-list') |
| | | url._all = list; |
| | | url._all = 'list'; |
| | | |
| | | this.goto_url('compose', url, true); |
| | | } |
| | |
| | | |
| | | this.doc_mouse_up = function(e) |
| | | { |
| | | var model, list, li, id; |
| | | var model, list, id; |
| | | |
| | | // ignore event if jquery UI dialog is open |
| | | if ($(rcube_event.get_target(e)).closest('.ui-dialog, .ui-widget-overlay').length) |
| | | return; |
| | | |
| | | if (list = this.message_list) { |
| | | if (!rcube_mouse_is_over(e, list.list.parentNode)) |
| | | list.blur(); |
| | | else |
| | | list.focus(); |
| | | if (list = this.message_list) |
| | | model = this.env.mailboxes; |
| | | } |
| | | else if (list = this.contact_list) { |
| | | if (!rcube_mouse_is_over(e, list.list.parentNode)) |
| | | list.blur(); |
| | | else |
| | | list.focus(); |
| | | else if (list = this.contact_list) |
| | | model = this.env.contactfolders; |
| | | } |
| | | else if (this.ksearch_value) { |
| | | else if (this.ksearch_value) |
| | | this.ksearch_blur(); |
| | | } |
| | | |
| | | if (list && !rcube_mouse_is_over(e, list.list.parentNode)) |
| | | list.blur(); |
| | | |
| | | // handle mouse release when dragging |
| | | if (this.drag_active && model && this.env.last_folder_target) { |
| | |
| | | if (list.multi_selecting || !this.env.contentframe) |
| | | return; |
| | | |
| | | if (list.get_single_selection() && window.frames && window.frames[this.env.contentframe]) { |
| | | if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)>=0) { |
| | | if (this.preview_timer) |
| | | clearTimeout(this.preview_timer); |
| | | if (this.preview_read_timer) |
| | | clearTimeout(this.preview_read_timer); |
| | | this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200); |
| | | } |
| | | if (list.get_single_selection()) |
| | | return; |
| | | |
| | | var win = this.get_frame_window(this.env.contentframe); |
| | | |
| | | if (win && win.location.href.indexOf(this.env.blankpage)>=0) { |
| | | if (this.preview_timer) |
| | | clearTimeout(this.preview_timer); |
| | | if (this.preview_read_timer) |
| | | clearTimeout(this.preview_read_timer); |
| | | this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200); |
| | | } |
| | | }; |
| | | |
| | |
| | | if (!id) |
| | | return; |
| | | |
| | | var target = window, |
| | | var win, target = window, |
| | | action = preview ? 'preview': 'show', |
| | | url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox); |
| | | |
| | | if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | if (preview && (win = this.get_frame_window(this.env.contentframe))) { |
| | | target = win; |
| | | url += '&_framed=1'; |
| | | } |
| | | |
| | |
| | | |
| | | this.show_contentframe = function(show) |
| | | { |
| | | var frm, win; |
| | | if (this.env.contentframe && (frm = $('#'+this.env.contentframe)) && frm.length) { |
| | | if (!show && (win = window.frames[this.env.contentframe])) { |
| | | var frame, win, name = this.env.contentframe; |
| | | |
| | | if (name && (frame = this.get_frame_element(name))) { |
| | | if (!show && (win = this.get_frame_window(name))) { |
| | | if (win.location && win.location.href.indexOf(this.env.blankpage)<0) |
| | | win.location.href = this.env.blankpage; |
| | | } |
| | | else if (!bw.safari && !bw.konq) |
| | | frm[show ? 'show' : 'hide'](); |
| | | } |
| | | $(frame)[show ? 'show' : 'hide'](); |
| | | } |
| | | |
| | | if (!show && this.busy) |
| | | this.set_busy(false, null, this.env.frame_lock); |
| | | }; |
| | | |
| | | this.get_frame_element = function(id) |
| | | { |
| | | var frame; |
| | | |
| | | if (id && (frame = document.getElementById(id))) |
| | | return frame; |
| | | }; |
| | | |
| | | this.get_frame_window = function(id) |
| | | { |
| | | var frame = this.get_frame_element(id); |
| | | |
| | | if (frame && frame.name && window.frames) |
| | | return window.frames[frame.name]; |
| | | }; |
| | | |
| | | this.lock_frame = function() |
| | |
| | | // list messages of a specific mailbox |
| | | this.list_mailbox = function(mbox, page, sort, url) |
| | | { |
| | | var target = window; |
| | | var win, target = window; |
| | | |
| | | if (typeof url != 'object') |
| | | url = {}; |
| | |
| | | return; |
| | | } |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | target = win; |
| | | url._framed = 1; |
| | | } |
| | | |
| | |
| | | // set a specific flag to one or more messages |
| | | this.mark_message = function(flag, uid) |
| | | { |
| | | var a_uids = [], r_uids = [], len, n, id, |
| | | selection = this.message_list ? this.message_list.get_selection() : []; |
| | | var a_uids = [], r_uids = [], len, n, id, selection, |
| | | list = this.message_list; |
| | | |
| | | if (uid) |
| | | a_uids[0] = uid; |
| | | else if (this.env.uid) |
| | | a_uids[0] = this.env.uid; |
| | | else if (this.message_list) { |
| | | else if (list) { |
| | | selection = list.get_selection(); |
| | | for (n=0, len=selection.length; n<len; n++) { |
| | | a_uids.push(selection[n]); |
| | | } |
| | | } |
| | | |
| | | if (!this.message_list) |
| | | if (!list) |
| | | r_uids = a_uids; |
| | | else |
| | | else { |
| | | list.focus(); |
| | | for (n=0, len=a_uids.length; n<len; n++) { |
| | | id = a_uids[n]; |
| | | if ((flag=='read' && this.message_list.rows[id].unread) |
| | | || (flag=='unread' && !this.message_list.rows[id].unread) |
| | | || (flag=='delete' && !this.message_list.rows[id].deleted) |
| | | || (flag=='undelete' && this.message_list.rows[id].deleted) |
| | | || (flag=='flagged' && !this.message_list.rows[id].flagged) |
| | | || (flag=='unflagged' && this.message_list.rows[id].flagged)) |
| | | if ((flag=='read' && list.rows[id].unread) |
| | | || (flag=='unread' && !list.rows[id].unread) |
| | | || (flag=='delete' && !list.rows[id].deleted) |
| | | || (flag=='undelete' && list.rows[id].deleted) |
| | | || (flag=='flagged' && !list.rows[id].flagged) |
| | | || (flag=='unflagged' && list.rows[id].flagged)) |
| | | { |
| | | r_uids.push(id); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // nothing to do |
| | | if (!r_uids.length && !this.select_all_mode) |
| | |
| | | input_message = $("[name='_message']"), |
| | | message = input_message.val(), |
| | | is_html = ($("input[name='_is_html']").val() == '1'), |
| | | sig = this.env.identity, |
| | | sig_separator = this.env.sig_above && (this.env.compose_mode == 'reply' || this.env.compose_mode == 'forward') ? '---' : '-- '; |
| | | sig = this.env.identity; |
| | | |
| | | // enable manual signature insert |
| | | if (this.env.signatures && this.env.signatures[id]) { |
| | |
| | | if (!is_html) { |
| | | // remove the 'old' signature |
| | | if (show_sig && sig && this.env.signatures && this.env.signatures[sig]) { |
| | | |
| | | sig = this.env.signatures[sig].is_html ? this.env.signatures[sig].plain_text : this.env.signatures[sig].text; |
| | | sig = this.env.signatures[sig].text; |
| | | sig = sig.replace(/\r\n/g, '\n'); |
| | | |
| | | if (!sig.match(/^--[ -]\n/m)) |
| | | sig = sig_separator + '\n' + sig; |
| | | |
| | | p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig); |
| | | if (p >= 0) |
| | |
| | | } |
| | | // add the new signature string |
| | | if (show_sig && this.env.signatures && this.env.signatures[id]) { |
| | | sig = this.env.signatures[id]['is_html'] ? this.env.signatures[id]['plain_text'] : this.env.signatures[id]['text']; |
| | | sig = this.env.signatures[id].text; |
| | | sig = sig.replace(/\r\n/g, '\n'); |
| | | |
| | | if (!sig.match(/^--[ -]\n/m)) |
| | | sig = sig_separator + '\n' + sig; |
| | | |
| | | if (this.env.sig_above) { |
| | | if (p >= 0) { // in place of removed signature |
| | |
| | | } |
| | | } |
| | | |
| | | if (this.env.signatures[id]) { |
| | | if (this.env.signatures[id].is_html) { |
| | | sig = this.env.signatures[id].text; |
| | | if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/m)) |
| | | sig = sig_separator + '<br />' + sig; |
| | | } |
| | | else { |
| | | sig = this.env.signatures[id].text; |
| | | if (!sig.match(/^--[ -]\r?\n/m)) |
| | | sig = sig_separator + '\n' + sig; |
| | | sig = '<pre>' + sig + '</pre>'; |
| | | } |
| | | |
| | | sigElem.innerHTML = sig; |
| | | } |
| | | if (this.env.signatures[id]) |
| | | sigElem.innerHTML = this.env.signatures[id].html; |
| | | } |
| | | |
| | | this.env.identity = id; |
| | |
| | | |
| | | // if a group is currently selected, and there is at least one contact selected |
| | | // thend we can enable the group-remove-selected command |
| | | this.enable_command('group-remove-selected', typeof this.env.group != 'undefined' && list.selection.length > 0); |
| | | |
| | | this.enable_command('group-remove-selected', this.env.group && list.selection.length > 0); |
| | | this.enable_command('compose', this.env.group || list.selection.length > 0); |
| | | this.enable_command('edit', id && writable); |
| | | this.enable_command('delete', list.selection.length && writable); |
| | |
| | | |
| | | this.list_contacts = function(src, group, page) |
| | | { |
| | | var folder, url = {}, |
| | | var win, folder, url = {}, |
| | | target = window; |
| | | |
| | | if (!src) |
| | |
| | | return; |
| | | } |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | target = win; |
| | | url._framed = 1; |
| | | } |
| | | |
| | |
| | | // load contact record |
| | | this.load_contact = function(cid, action, framed) |
| | | { |
| | | var url = {}, target = window; |
| | | var win, url = {}, target = window; |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | url._framed = 1; |
| | | target = window.frames[this.env.contentframe]; |
| | | target = win; |
| | | this.show_contentframe(true); |
| | | |
| | | // load dummy content |
| | |
| | | // load advanced search page |
| | | this.advanced_search = function() |
| | | { |
| | | var url = {_form: 1, _action: 'search'}, target = window; |
| | | var win, url = {_form: 1, _action: 'search'}, target = window; |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | url._framed = 1; |
| | | target = window.frames[this.env.contentframe]; |
| | | target = win; |
| | | this.contact_list.clear_selection(); |
| | | } |
| | | |
| | |
| | | // preferences section select and load options frame |
| | | this.section_select = function(list) |
| | | { |
| | | var id = list.get_single_selection(), target = window, |
| | | var win, id = list.get_single_selection(), target = window, |
| | | url = {_action: 'edit-prefs', _section: id}; |
| | | |
| | | if (id) { |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | url._framed = 1; |
| | | target = window.frames[this.env.contentframe]; |
| | | target = win; |
| | | } |
| | | this.location_href(url, target, true); |
| | | } |
| | |
| | | if (action == 'edit-identity' && (!id || id == this.env.iid)) |
| | | return false; |
| | | |
| | | var target = window, |
| | | var win, target = window, |
| | | url = {_action: action, _iid: id}; |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | url._framed = 1; |
| | | target = window.frames[this.env.contentframe]; |
| | | document.getElementById(this.env.contentframe).style.visibility = 'inherit'; |
| | | target = win; |
| | | } |
| | | |
| | | if (action && (id || action == 'add-identity')) { |
| | |
| | | // when user select a folder in manager |
| | | this.show_folder = function(folder, path, force) |
| | | { |
| | | var target = window, |
| | | var win, target = window, |
| | | url = '&_action=edit-folder&_mbox='+urlencode(folder); |
| | | |
| | | if (path) |
| | | url += '&_path='+urlencode(path); |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { |
| | | target = window.frames[this.env.contentframe]; |
| | | if (win = this.get_frame_window(this.env.contentframe)) { |
| | | target = win; |
| | | url += '&_framed=1'; |
| | | } |
| | | |
| | |
| | | return 0; |
| | | }; |
| | | |
| | | // Cookie setter |
| | | this.set_cookie = function(name, value, expires) |
| | | { |
| | | setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure); |
| | | } |
| | | |
| | | } // end object rcube_webmail |
| | | |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | rcube_webmail.prototype.get_cookie = getCookie; |
| | | |
| | | // copy event engine prototype |
| | | rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; |
| | | rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; |
| | |
| | | atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+', |
| | | quoted_pair = '\\x5c[\\x00-\\x7f]', |
| | | quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22', |
| | | ipv4 = '\\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\\]', |
| | | ipv6 = '\\[IPv6:[0-9a-f:.]+\\]', |
| | | ip_addr = '(' + ipv4 + ')|(' + ipv6 + ')', |
| | | // Use simplified domain matching, because we need to allow Unicode characters here |
| | | // So, e-mail address should be validated also on server side after idn_to_ascii() use |
| | | //domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d', |
| | | //sub_domain = '('+atom+'|'+domain_literal+')', |
| | | // allow punycode/unicode top-level domain |
| | | domain = '([^@\\x2e]+\\x2e)+([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,})', |
| | | domain = '(('+ip_addr+')|(([^@\\x2e]+\\x2e)+([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,})))', |
| | | // ICANN e-mail test (http://idn.icann.org/E-mail_test) |
| | | icann_domains = [ |
| | | '\\u0645\\u062b\\u0627\\u0644\\x2e\\u0625\\u062e\\u062a\\u0628\\u0627\\u0631', |
| | |
| | | |
| | | return false; |
| | | }; |
| | | |
| | | |
| | | // recursively copy an object |
| | | function rcube_clone_object(obj) |
| | |
| | | return unescape(dc.substring(begin + prefix.length, end)); |
| | | }; |
| | | |
| | | // deprecated aliases, to be removed, use rcmail.set_cookie/rcmail.get_cookie |
| | | roundcube_browser.prototype.set_cookie = setCookie; |
| | | roundcube_browser.prototype.get_cookie = getCookie; |
| | | |
| | |
| | | function GoogieSpell(img_dir, server_url, has_dict) |
| | | { |
| | | var ref = this, |
| | | cookie_value = getCookie('language'); |
| | | cookie_value = rcmail.get_cookie('language'); |
| | | |
| | | GOOGIE_CUR_LANG = cookie_value != null ? cookie_value : GOOGIE_DEFAULT_LANG; |
| | | |
| | |
| | | //Set cookie |
| | | var now = new Date(); |
| | | now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); |
| | | setCookie('language', lan_code, now); |
| | | rcmail.set_cookie('language', lan_code, now); |
| | | }; |
| | | |
| | | this.setForceWidthHeight = function(width, height) |
| | |
| | | } |
| | | } |
| | | |
| | | // Un-focus already focused elements |
| | | $(document.activeElement).blur(); |
| | | // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620) |
| | | $(':focus:not(body)').blur(); |
| | | $('iframe').each(function() { this.blur(); }); |
| | | |
| | | if (e || (e = window.event)) |
| | |
| | | * out that extra spaces should be compressed--a problem addressed with |
| | | * Marcus Bointon's fixes but that I had not yet incorporated. |
| | | * |
| | | * Thanks to Daniel Schledermann (http://www.typoconsult.dk/) for |
| | | * Thanks to Daniel Schledermann (http://www.typoconsult.dk/) for |
| | | * suggesting a valuable fix with <a> tag handling. |
| | | * |
| | | * Thanks to Wojciech Bajon (again!) for suggesting fixes and additions, |
| | |
| | | var $ent_search = array( |
| | | '/&(nbsp|#160);/i', // Non-breaking space |
| | | '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i', |
| | | // Double quotes |
| | | // Double quotes |
| | | '/&(apos|rsquo|lsquo|#8216|#8217);/i', // Single quotes |
| | | '/>/i', // Greater-than |
| | | '/</i', // Less-than |
| | |
| | | function set_base_url( $url = '' ) |
| | | { |
| | | if ( empty($url) ) { |
| | | if ( !empty($_SERVER['HTTP_HOST']) ) { |
| | | $this->url = 'http://' . $_SERVER['HTTP_HOST']; |
| | | } else { |
| | | $this->url = ''; |
| | | } |
| | | if ( !empty($_SERVER['HTTP_HOST']) ) { |
| | | $this->url = 'http://' . $_SERVER['HTTP_HOST']; |
| | | } else { |
| | | $this->url = ''; |
| | | } |
| | | } else { |
| | | // Strip any trailing slashes for consistency (relative |
| | | // URLs may already start with a slash like "/file.html") |
| | |
| | | $text = preg_replace($this->ent_search, $this->ent_replace, $text); |
| | | |
| | | // Replace known html entities |
| | | $text = html_entity_decode($text, ENT_COMPAT, 'UTF-8'); |
| | | $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); |
| | | |
| | | // Remove unknown/unhandled entities (this cannot be done in search-and-replace block) |
| | | $text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text); |
| | |
| | | // for PHP versions >= 4.0.2. Default width is 75 |
| | | // If width is 0 or less, don't wrap the text. |
| | | if ( $this->width > 0 ) { |
| | | $text = wordwrap($text, $this->width); |
| | | $text = wordwrap($text, $this->width); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | function _build_link_list( $link, $display ) |
| | | { |
| | | if (!$this->_do_links || empty($link)) { |
| | | return $display; |
| | | } |
| | | |
| | | // Ignored link types |
| | | if (preg_match('!^(javascript:|mailto:|#)!i', $link)) { |
| | | return $display; |
| | | if (!$this->_do_links || empty($link)) { |
| | | return $display; |
| | | } |
| | | |
| | | if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) { |
| | | // Ignored link types |
| | | if (preg_match('!^(javascript:|mailto:|#)!i', $link)) { |
| | | return $display; |
| | | } |
| | | |
| | | if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) { |
| | | $url = $link; |
| | | } |
| | | else { |
| | |
| | | $key = strtolower($key); |
| | | $value = $node->getAttribute($key); |
| | | if (isset($this->_html_attribs[$key]) || |
| | | ($key == 'href' && preg_match('!^([a-z][a-z0-9.+-]+:|//|#).+!i', $value))) |
| | | ($key == 'href' && !preg_match('!^javascript!i', $value) |
| | | && preg_match('!^([a-z][a-z0-9.+-]+:|//|#).+!i', $value)) |
| | | ) { |
| | | $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; |
| | | } |
| | | else if ($key == 'style' && ($style = $this->wash_style($value))) { |
| | | $quot = strpos($style, '"') !== false ? "'" : '"'; |
| | | $t .= ' style=' . $quot . $style . $quot; |
| | |
| | | else if (preg_match('/^data:.+/i', $value)) { // RFC2397 |
| | | $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; |
| | | } |
| | | } else |
| | | } |
| | | else |
| | | $washed .= ($washed?' ':'') . $key; |
| | | } |
| | | return $t . ($washed && $this->config['show_washed']?' x-washed="'.$washed.'"':''); |
| | |
| | | <?php |
| | | |
| | | /* |
| | | |
| | | +-----------------------------------------------------------------------+ |
| | | | language/en_US/labels.inc | |
| | | | | |
| | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | @version $Id$ |
| | | |
| | | */ |
| | | |
| | | $labels = array(); |
| | |
| | | $labels['flagged'] = 'Flagged'; |
| | | $labels['unanswered'] = 'Unanswered'; |
| | | $labels['deleted'] = 'Deleted'; |
| | | $labels['undeleted'] = 'Not deleted'; |
| | | $labels['invert'] = 'Invert'; |
| | | $labels['filter'] = 'Filter'; |
| | | $labels['list'] = 'List'; |
| | |
| | | $labels['signature'] = 'Signature'; |
| | | $labels['dstactive'] = 'Daylight saving time'; |
| | | $labels['htmleditor'] = 'Compose HTML messages'; |
| | | $labels['htmlonreply'] = 'on reply to HTML message only'; |
| | | $labels['htmlonreply'] = 'on reply to HTML message'; |
| | | $labels['htmlonreplyandforward'] = 'on forward or reply to HTML message'; |
| | | $labels['htmlsignature'] = 'HTML signature'; |
| | | $labels['previewpane'] = 'Show preview pane'; |
| | | $labels['skin'] = 'Interface skin'; |
| | |
| | | $labels['newmessage'] = 'New Message'; |
| | | $labels['signatureoptions'] = 'Signature Options'; |
| | | $labels['whenreplying'] = 'When replying'; |
| | | $labels['replytopposting'] = 'start new message above original'; |
| | | $labels['replybottomposting'] = 'start new message below original'; |
| | | $labels['replyempty'] = 'do not quote the original message'; |
| | | $labels['replytopposting'] = 'start new message above the quote'; |
| | | $labels['replybottomposting'] = 'start new message below the quote'; |
| | | $labels['replyremovesignature'] = 'When replying remove original signature from message'; |
| | | $labels['autoaddsignature'] = 'Automatically add signature'; |
| | | $labels['newmessageonly'] = 'new message only'; |
| | |
| | | $labels['flagged'] = 'Oznaczone'; |
| | | $labels['unanswered'] = 'Bez odpowiedzi'; |
| | | $labels['deleted'] = 'Usunięte'; |
| | | $labels['undeleted'] = 'Nieusunięte'; |
| | | $labels['invert'] = 'Odwróć'; |
| | | $labels['filter'] = 'Filtr'; |
| | | $labels['list'] = 'Lista'; |
| | |
| | | foreach ($row as $key => $values) { |
| | | list($field, $section) = explode(':', $key); |
| | | foreach ((array)$values as $value) { |
| | | if (is_array($value) || strlen($value)) |
| | | if (is_array($value) || @strlen($value)) |
| | | $vcard->set($field, $value, strtoupper($section)); |
| | | } |
| | | } |
| | |
| | | $OUTPUT->command('set_env', 'source', ''); |
| | | $OUTPUT->command('set_env', 'group', ''); |
| | | |
| | | // unselect currently selected directory/group |
| | | if (!$sid) |
| | | if (!$sid) { |
| | | // unselect currently selected directory/group |
| | | $OUTPUT->command('unselect_directory'); |
| | | // enable "Save search" command |
| | | $OUTPUT->command('enable_command', 'search-create', true); |
| | | } |
| | | $OUTPUT->command('update_group_commands'); |
| | | |
| | | // send response |
| | |
| | | // set current mailbox in client environment |
| | | $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); |
| | | $OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false)); |
| | | $OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false)); |
| | | $OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0); |
| | | $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); |
| | | |
| | | // default font for HTML editor |
| | |
| | | if (count($MESSAGE->identities)) |
| | | { |
| | | foreach ($MESSAGE->identities as $idx => $ident) { |
| | | $email = mb_strtolower(rcube_idn_to_utf8($ident['email'])); |
| | | $ident['email'] = format_email($ident['email']); |
| | | $email = format_email(rcube_idn_to_utf8($ident['email'])); |
| | | |
| | | $MESSAGE->identities[$idx]['email_ascii'] = $ident['email']; |
| | | $MESSAGE->identities[$idx]['ident'] = format_email_recipient($ident['email'], $ident['name']); |
| | |
| | | $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); |
| | | foreach ($a_to as $addr) { |
| | | if (!empty($addr['mailto'])) { |
| | | $a_recipients[] = strtolower($addr['mailto']); |
| | | $a_recipients[] = format_email($addr['mailto']); |
| | | $a_names[] = $addr['name']; |
| | | } |
| | | } |
| | |
| | | $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); |
| | | foreach ($a_cc as $addr) { |
| | | if (!empty($addr['mailto'])) { |
| | | $a_recipients[] = strtolower($addr['mailto']); |
| | | $a_recipients[] = format_email($addr['mailto']); |
| | | $a_names[] = $addr['name']; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | $from_idx = null; |
| | | $default_identity = null; |
| | | $found_idx = null; |
| | | $default_identity = 0; // default identity is always first on the list |
| | | $return_path = $MESSAGE->headers->others['return-path']; |
| | | |
| | | // Select identity |
| | | foreach ($MESSAGE->identities as $idx => $ident) { |
| | | // save default identity ID |
| | | if ($ident['standard']) { |
| | | $default_identity = $idx; |
| | | } |
| | | |
| | | // use From header |
| | | if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { |
| | | if ($MESSAGE->headers->from == $ident['ident']) { |
| | |
| | | } |
| | | // use replied message recipients |
| | | else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { |
| | | // match identity name, prefer default identity |
| | | if ($from_idx === null || ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name'])) { |
| | | if ($found_idx === null) { |
| | | $found_idx = $idx; |
| | | } |
| | | // match identity name |
| | | if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) { |
| | | $from_idx = $idx; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If matching by name+address doesn't found any amtches, get first found address (identity) |
| | | if ($from_idx === null) { |
| | | $from_idx = $found_idx; |
| | | } |
| | | |
| | | // Fallback using Return-Path |
| | |
| | | } |
| | | } |
| | | |
| | | // Still no ID, use default/first identity |
| | | if ($from_idx === null) { |
| | | $from_idx = $default_identity !== null ? $default_identity : key(reset($MESSAGE->identities)); |
| | | } |
| | | |
| | | $ident = $MESSAGE->identities[$from_idx]; |
| | | $ident = $MESSAGE->identities[$from_idx !== null ? $from_idx : $default_identity]; |
| | | $from_id = $ident['identity_id']; |
| | | |
| | | $MESSAGE->compose['from_email'] = $ident['email']; |
| | |
| | | if (empty($addr_part['mailto'])) |
| | | continue; |
| | | |
| | | $mailto = mb_strtolower(rcube_idn_to_utf8($addr_part['mailto'])); |
| | | $mailto = format_email(rcube_idn_to_utf8($addr_part['mailto'])); |
| | | |
| | | if (!in_array($mailto, $a_recipients) |
| | | && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email']) |
| | |
| | | |
| | | function rcmail_compose_header_from($attrib) |
| | | { |
| | | global $MESSAGE, $OUTPUT; |
| | | global $MESSAGE, $OUTPUT, $RCMAIL, $compose_mode; |
| | | |
| | | // pass the following attributes to the form class |
| | | $field_attrib = array('name' => '_from'); |
| | |
| | | if (count($MESSAGE->identities)) |
| | | { |
| | | $a_signatures = array(); |
| | | $separator = $RCMAIL->config->get('sig_above') |
| | | && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD) ? '---' : '-- '; |
| | | |
| | | $field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)"; |
| | | $select_from = new html_select($field_attrib); |
| | |
| | | // add signature to array |
| | | if (!empty($sql_arr['signature']) && empty($COMPOSE['param']['nosig'])) |
| | | { |
| | | $a_signatures[$identity_id]['text'] = $sql_arr['signature']; |
| | | $a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false; |
| | | if ($a_signatures[$identity_id]['is_html']) |
| | | { |
| | | $h2t = new html2text($a_signatures[$identity_id]['text'], false, false); |
| | | $a_signatures[$identity_id]['plain_text'] = trim($h2t->get_text()); |
| | | $text = $html = $sql_arr['signature']; |
| | | |
| | | if ($sql_arr['html_signature']) { |
| | | $h2t = new html2text($sql_arr['signature'], false, false); |
| | | $text = trim($h2t->get_text()); |
| | | } |
| | | else { |
| | | $html = htmlentities($html, ENT_NOQUOTES, RCMAIL_CHARSET); |
| | | } |
| | | |
| | | if (!preg_match('/^--[ -]\r?\n/m', $text)) { |
| | | $text = $separator . "\n" . $text; |
| | | $html = $separator . "<br>" . $html; |
| | | } |
| | | |
| | | if (!$sql_arr['html_signature']) { |
| | | $html = "<pre>" . $html . "</pre>"; |
| | | } |
| | | |
| | | $a_signatures[$identity_id]['text'] = $text; |
| | | $a_signatures[$identity_id]['html'] = $html; |
| | | } |
| | | } |
| | | |
| | |
| | | $useHtml = $MESSAGE->has_html_part(false); |
| | | } |
| | | else if ($compose_mode == RCUBE_COMPOSE_REPLY) { |
| | | $useHtml = ($html_editor == 1 || ($html_editor == 2 && $MESSAGE->has_html_part(false))); |
| | | $useHtml = ($html_editor == 1 || ($html_editor >= 2 && $MESSAGE->has_html_part(false))); |
| | | } |
| | | else { // RCUBE_COMPOSE_FORWARD or NEW |
| | | else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { |
| | | $useHtml = ($html_editor == 1 || ($html_editor == 3 && $MESSAGE->has_html_part(false))); |
| | | } |
| | | else { |
| | | $useHtml = ($html_editor == 1); |
| | | } |
| | | |
| | |
| | | rcmail_write_forward_attachment($MESSAGE); |
| | | } |
| | | // reply/edit/draft/forward |
| | | else if ($compose_mode) { |
| | | else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { |
| | | $isHtml = rcmail_compose_editor_mode(); |
| | | |
| | | if (!empty($MESSAGE->parts)) { |
| | |
| | | $prefix .= "\n"; |
| | | $suffix = ''; |
| | | |
| | | if ($RCMAIL->config->get('top_posting')) |
| | | if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting |
| | | $prefix = "\n\n\n" . $prefix; |
| | | } |
| | | } |
| | | else { |
| | | // save inline images to files |
| | |
| | | $prefix = '<p>' . Q($prefix) . "</p>\n"; |
| | | $prefix .= '<blockquote>'; |
| | | |
| | | if ($RCMAIL->config->get('top_posting')) { |
| | | if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting |
| | | $prefix = '<br>' . $prefix; |
| | | $suffix = '</blockquote>'; |
| | | } |
| | |
| | | // set current mailbox and some other vars in client environment |
| | | $OUTPUT->set_env('mailbox', $mbox_name); |
| | | $OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize()); |
| | | $OUTPUT->set_env('quota', $RCMAIL->storage->get_capability('QUOTA')); |
| | | $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter()); |
| | | $OUTPUT->set_env('threading', $threading); |
| | | $OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD')); |
| | | $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0)); |
| | | if ($RCMAIL->storage->get_capability('QUOTA')) { |
| | | $OUTPUT->set_env('quota', true); |
| | | } |
| | | |
| | | if ($CONFIG['delete_junk']) |
| | | $OUTPUT->set_env('delete_junk', true); |
| | |
| | | global $OUTPUT; |
| | | |
| | | $html = html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), '')); |
| | | $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), ''); |
| | | |
| | | if (!get_boolean($attrib['no-switch'])) { |
| | | $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), ''); |
| | | } |
| | | |
| | | unset($attrib['no-switch']); |
| | | |
| | | $OUTPUT->add_gui_object('all_headers_row', 'all-headers'); |
| | | $OUTPUT->add_gui_object('all_headers_box', 'headers-source'); |
| | | |
| | | return html::div($attrib, $html); |
| | | return count($attrib) > 1 ? html::div($attrib, $html) : $html; |
| | | } |
| | | |
| | | |
| | |
| | | $select_filter->add(rcube_label('unread'), 'UNSEEN'); |
| | | $select_filter->add(rcube_label('flagged'), 'FLAGGED'); |
| | | $select_filter->add(rcube_label('unanswered'), 'UNANSWERED'); |
| | | if (!$CONFIG['skip_deleted']) |
| | | if (!$CONFIG['skip_deleted']) { |
| | | $select_filter->add(rcube_label('deleted'), 'DELETED'); |
| | | $select_filter->add(rcube_label('undeleted'), 'UNDELETED'); |
| | | } |
| | | $select_filter->add(rcube_label('priority').': '.rcube_label('highest'), 'HEADER X-PRIORITY 1'); |
| | | $select_filter->add(rcube_label('priority').': '.rcube_label('high'), 'HEADER X-PRIORITY 2'); |
| | | $select_filter->add(rcube_label('priority').': '.rcube_label('normal'), 'NOT HEADER X-PRIORITY 1 NOT HEADER X-PRIORITY 2 NOT HEADER X-PRIORITY 4 NOT HEADER X-PRIORITY 5'); |
| | |
| | | $source = $RCMAIL->storage->get_raw_headers($uid); |
| | | |
| | | if ($source !== false) { |
| | | $source = htmlspecialchars(trim($source)); |
| | | $source = trim(rcube_charset::clean($source)); |
| | | $source = htmlspecialchars($source); |
| | | $source = preg_replace( |
| | | array( |
| | | '/\n[\t\s]+/', |
| | |
| | | $h2t = new html2text($plugin['body'], false, true, 0); |
| | | $plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n"); |
| | | $plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true); |
| | | if (!$plainTextPart) { |
| | | // empty message body breaks attachment handling in drafts |
| | | $plainTextPart = "\r\n"; |
| | | } |
| | | else { |
| | | // make sure all line endings are CRLF (#1486712) |
| | | $plainTextPart = preg_replace('/\r?\n/', "\r\n", $plainTextPart); |
| | | } |
| | | |
| | | // make sure all line endings are CRLF (#1486712) |
| | | $plainTextPart = preg_replace('/\r?\n/', "\r\n", $plainTextPart); |
| | | |
| | | $plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body', |
| | | array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME)); |
| | |
| | | $message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n"); |
| | | |
| | | $message_body = wordwrap($message_body, 998, "\r\n", true); |
| | | if (!strlen($message_body)) { |
| | | // empty message body breaks attachment handling in drafts |
| | | $message_body = "\r\n"; |
| | | } |
| | | |
| | | $MAIL_MIME->setTXTBody($message_body, false, true); |
| | | } |
| | |
| | | header("Content-Disposition: attachment; filename=\"$filename\""); |
| | | } |
| | | |
| | | $RCMAIL->storage->print_raw_body($uid); |
| | | $RCMAIL->storage->print_raw_body($uid, empty($_GET['_save'])); |
| | | } |
| | | else |
| | | { |
| | |
| | | |
| | | if ($result) { |
| | | // Handle subscription of protected folder (#1487656) |
| | | if ($CONFIG['protect_default_folders'] == true |
| | | && in_array($mbox, $CONFIG['default_folders']) |
| | | if ($RCMAIL->config->get('protect_default_folders') |
| | | && in_array($mbox, (array)$RCMAIL->config->get('default_folders')) |
| | | ) { |
| | | $OUTPUT->command('disable_subscription', $mbox); |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | // check if the folder is shared, then disable subscription option on it |
| | | if (!$disabled && $folder['virtual'] && !empty($namespace)) { |
| | | // check if the folder is shared, then disable subscription option on it (if not subscribed already) |
| | | if (!$disabled && !$subscribed && $folder['virtual'] && !empty($namespace)) { |
| | | $tmp_ns = array_merge((array)$namespace['other'], (array)$namespace['shared']); |
| | | foreach ($tmp_ns as $item) { |
| | | if (strpos($folder['id'], $item[0]) === 0) { |
| | |
| | | |
| | | $OUTPUT->set_pagetitle(rcube_label('folders')); |
| | | $OUTPUT->include_script('list.js'); |
| | | $OUTPUT->set_env('quota', $STORAGE->get_capability('QUOTA')); |
| | | $OUTPUT->set_env('prefix_ns', $STORAGE->get_namespace('prefix')); |
| | | if ($STORAGE->get_capability('QUOTA')) { |
| | | $OUTPUT->set_env('quota', true); |
| | | } |
| | | |
| | | // add some labels to client |
| | | $OUTPUT->add_label('deletefolderconfirm', 'purgefolderconfirm', 'folderdeleting', |
| | |
| | | $select_htmleditor->add(rcube_label('never'), 0); |
| | | $select_htmleditor->add(rcube_label('always'), 1); |
| | | $select_htmleditor->add(rcube_label('htmlonreply'), 2); |
| | | $select_htmleditor->add(rcube_label('htmlonreplyandforward'), 3); |
| | | |
| | | $blocks['main']['options']['htmleditor'] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label('htmleditor'))), |
| | |
| | | ); |
| | | } |
| | | |
| | | if (!isset($no_override['top_posting'])) { |
| | | $field_id = 'rcmfd_top_posting'; |
| | | $select_replymode = new html_select(array('name' => '_top_posting', 'id' => $field_id, |
| | | 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex==0)")); |
| | | if (!isset($no_override['reply_mode'])) { |
| | | $field_id = 'rcmfd_reply_mode'; |
| | | $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id, |
| | | 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex<2)")); |
| | | $select_replymode->add(rcube_label('replyempty'), -1); |
| | | $select_replymode->add(rcube_label('replybottomposting'), 0); |
| | | $select_replymode->add(rcube_label('replytopposting'), 1); |
| | | |
| | | $blocks['main']['options']['top_posting'] = array( |
| | | $blocks['main']['options']['reply_mode'] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label('whenreplying'))), |
| | | 'content' => $select_replymode->show($config['top_posting']?1:0), |
| | | 'content' => $select_replymode->show(intval($config['reply_mode'])), |
| | | ); |
| | | } |
| | | |
| | |
| | | |
| | | if (!isset($no_override['sig_above'])) { |
| | | $field_id = 'rcmfd_sig_above'; |
| | | $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => !$config['top_posting'])); |
| | | $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => $config['reply_mode'] < 1)); |
| | | $select_sigabove->add(rcube_label('belowquote'), 0); |
| | | $select_sigabove->add(rcube_label('abovequote'), 1); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | if (!$error) { |
| | | if ($error) { |
| | | $OUTPUT->command('display_message', $error, 'error'); |
| | | } |
| | | else { |
| | | $folder['name'] = $name_imap; |
| | | $folder['oldname'] = $old_imap; |
| | | $folder['class'] = ''; |
| | |
| | | 'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE, |
| | | 'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE, |
| | | 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, |
| | | 'top_posting' => !empty($_POST['_top_posting']), |
| | | 'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0, |
| | | 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), |
| | | 'sig_above' => !empty($_POST['_sig_above']) && !empty($_POST['_top_posting']), |
| | | 'sig_above' => !empty($_POST['_sig_above']) && $_POST['_reply_mode'] < 1, |
| | | 'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST), |
| | | 'forward_attachment' => !empty($_POST['_forward_attachment']), |
| | | ); |
| | |
| | | $('a', tab).removeAttr('onclick').click(function() { return false; }); |
| | | } |
| | | |
| | | function rcube_show_advanced(visible) |
| | | { |
| | | $('tr.advanced').css('display', (visible ? (bw.ie ? 'block' : 'table-row') : 'none')); |
| | | } |
| | | |
| | | // Fieldsets-to-tabs converter |
| | | // Warning: don't place "caller" <script> inside page element (id) |
| | | function rcube_init_tabs(id, current) |
| | |
| | | if ((row = document.getElementById('compose-' + id))) { |
| | | var div = document.getElementById('compose-div'), |
| | | headers_div = document.getElementById('compose-headers-div'); |
| | | row.style.display = (document.all && !window.opera) ? 'block' : 'table-row'; |
| | | $(row).show(); |
| | | div.style.top = (parseInt(headers_div.offsetHeight, 10) + 3) + 'px'; |
| | | this.resize_compose_body(); |
| | | } |
| | |
| | | for (var i=0; i<links.length; i++) |
| | | if (links[i].style.display != 'none') |
| | | for (var j=i+1; j<links.length; j++) |
| | | if (links[j].style.display != 'none') |
| | | if (links[j].style.display != 'none') |
| | | if ((ns = this.next_sibling(links[i]))) { |
| | | ns.style.display = ''; |
| | | break; |
| | | } |
| | | ns.style.display = ''; |
| | | break; |
| | | } |
| | | |
| | | document.getElementById('_' + id).value = ''; |
| | | |
| | |
| | | <meta http-equiv="X-UA-Compatible" content="IE=EDGE" /> |
| | | <link rel="index" href="$__comm_path" /> |
| | | <link rel="shortcut icon" href="/images/favicon.ico"/> |
| | | <link rel="stylesheet" type="text/css" href="/common.css" /> |
| | |
| | | rcube_event.add_listener({element: window, event:'resize', object:this, method:'onResize'}); |
| | | |
| | | // read saved position from cookie |
| | | var cookie = bw.get_cookie(this.id); |
| | | var cookie = rcmail.get_cookie(this.id); |
| | | if (cookie && !isNaN(cookie)) { |
| | | this.pos = parseFloat(cookie); |
| | | this.resize(); |
| | |
| | | { |
| | | var exp = new Date(); |
| | | exp.setYear(exp.getFullYear() + 1); |
| | | bw.set_cookie(this.id, this.pos, exp); |
| | | rcmail.set_cookie(this.id, this.pos, exp); |
| | | }; |
| | | |
| | | } // end class rcube_splitter |
| | |
| | | <a href="#bcc" onclick="return rcmail_ui.hide_header_form('bcc');"><img src="/images/icons/minus.gif" alt="" width="13" height="11" title="<roundcube:label name='delete' />" /></a> |
| | | <label for="_bcc"><roundcube:label name="bcc" /></label> |
| | | </td> |
| | | <td colspan="2" class="editfield"><roundcube:object name="composeHeaders" part="bcc" form="form" id="_bcc" cols="70" rows="2" tabindex="4" /></td> |
| | | <td class="editfield"><roundcube:object name="composeHeaders" part="bcc" form="form" id="_bcc" cols="70" rows="2" tabindex="4" /></td> |
| | | </tr><tr id="compose-replyto"> |
| | | <td class="title top"> |
| | | <a href="#replyto" onclick="return rcmail_ui.hide_header_form('replyto');"><img src="/images/icons/minus.gif" alt="" width="13" height="11" title="<roundcube:label name='delete' />" /></a> |
| | |
| | | <div id="mailboxlist-container"> |
| | | <div id="mailboxlist-title" class="boxtitle"><roundcube:label name="mailboxlist" /></div> |
| | | <div class="boxlistcontent"> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" /> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" /> |
| | | </div> |
| | | <div class="boxfooter"> |
| | | <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " /> |
| | | </div> |
| | | <div class="boxfooter"></div> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165}); |
| | | rcmail.add_onload('mailviewsplitv.init()'); |
| | | </script> |
| | | |
| | | <div id="mailboxoptionsmenu" class="popupmenu"> |
| | | <ul> |
| | | <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li> |
| | | <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li> |
| | | <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li> |
| | | <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" /> |
| | | </ul> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |
| | |
| | | <div id="mailboxlist-container"> |
| | | <div class="boxtitle"><roundcube:label name="mailboxlist" /></div> |
| | | <div class="boxlistcontent"> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" /> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" /> |
| | | </div> |
| | | <div class="boxfooter"> |
| | | <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " /> |
| | | </div> |
| | | <div class="boxfooter"></div> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165}); |
| | | rcmail.add_onload('mailviewsplitv.init()'); |
| | | </script> |
| | | |
| | | <div id="mailboxoptionsmenu" class="popupmenu"> |
| | | <ul> |
| | | <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li> |
| | | <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li> |
| | | <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li> |
| | | <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" /> |
| | | </ul> |
| | | </div> |
| | | |
| | | </body> |
| | | <roundcube:endif /> |
| | |
| | | position: absolute; |
| | | top: -6px; |
| | | left: 0; |
| | | right: 260px; |
| | | height: 40px; |
| | | white-space: nowrap; |
| | | z-index: 10; |
| | |
| | | * License. It is allowed to copy, distribute, transmit and to adapt the work |
| | | * by keeping credits to the original autors in the README file. |
| | | * See http://creativecommons.org/licenses/by-sa/3.0/ for details. |
| | | * |
| | | * $Id$ |
| | | */ |
| | | |
| | | /* #1488618 */ |
| | | #mainscreen { |
| | | height: expression((parseInt(document.documentElement.clientHeight)-108)+'px'); |
| | | } |
| | | #mainscreen.offset { |
| | | height: expression((parseInt(document.documentElement.clientHeight)-150)+'px'); |
| | | } |
| | | |
| | | input.button { |
| | | display: inline; |
| | |
| | | .boxfooter .listbutton .inner, |
| | | .attachmentslist li a.delete, |
| | | .attachmentslist li a.cancelupload, |
| | | #messagepreviewheader .iconlink { |
| | | #messageheader .iconlink { |
| | | /* workaround for text-indent which also offsets the background image */ |
| | | text-indent: 0; |
| | | font-size: 0; |
| | |
| | | |
| | | .pagenav a.button, |
| | | .pagenav a.button span.inner, |
| | | #messagepreviewheader .iconlink, |
| | | #messageheader .iconlink, |
| | | #uploadform a.iconlink { |
| | | display: inline; |
| | | } |
| | |
| | | text-align: left; |
| | | } |
| | | |
| | | #messagepreviewheader .iconlink { |
| | | #messageheader .iconlink { |
| | | color: #fff; |
| | | height: 14px; |
| | | } |
| | |
| | | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#005d76', endColorstr='#004558', GradientType=0); |
| | | } |
| | | |
| | | #messageheader, #partheader, #composeheaders { |
| | | #partheader, #composeheaders { |
| | | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e9e9e9', GradientType=0); |
| | | } |
| | | |
| | |
| | | <meta http-equiv="X-UA-Compatible" content="IE=EDGE" /> |
| | | <link rel="shortcut icon" href="/images/favicon.ico"/> |
| | | <link rel="stylesheet" type="text/css" href="/styles.css" /> |
| | | <roundcube:if condition="in_array(env:task, array('mail','addressbook','settings'))" /> |
| | |
| | | bottom: 28px; |
| | | } |
| | | |
| | | #mailview-top.fullheight { |
| | | border-radius: 4px 4px 0 0; |
| | | } |
| | | |
| | | #mailview-bottom { |
| | | position: absolute; |
| | | left: 0; |
| | | bottom: 0; |
| | | width: 100%; |
| | | height: 26px; |
| | | } |
| | | |
| | | #mailview-top.fullheight { |
| | | border-radius: 4px 4px 0 0; |
| | | } |
| | | |
| | | #folderlist-header { |
| | |
| | | #messagetoolbar { |
| | | position: absolute; |
| | | top: -6px; |
| | | right: 390px; |
| | | left: 0; |
| | | height: 40px; |
| | | white-space: nowrap; |
| | |
| | | position: absolute; |
| | | right: 0; |
| | | top: 0; |
| | | width: 240px; |
| | | width: 400px; |
| | | } |
| | | |
| | | #mailpreviewtoggle { |
| | |
| | | /*** message list ***/ |
| | | |
| | | #messagelist thead td:first-child { |
| | | border-radius: 4px 0 0 0; |
| | | } |
| | | |
| | | #messagelist thead td:last-child { |
| | | border-radius: 0 4px 0 0; |
| | | border-radius: 4px 0 0 0; /* for Chrome */ |
| | | } |
| | | |
| | | #messagelist tr td.attachment, |
| | |
| | | |
| | | #messagecontent { |
| | | position: absolute; |
| | | top: 140px; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | bottom: 0; |
| | | bottom: 28px; |
| | | overflow: auto; |
| | | border-radius: 4px 4px 0 0; |
| | | } |
| | | |
| | | #messageheader, |
| | | #partheader, |
| | | #composeheaders { |
| | | position: relative; |
| | |
| | | |
| | | h3.subject { |
| | | font-size: 14px; |
| | | margin: 0 8em 0 0; |
| | | margin: 0 13em 0 0; |
| | | padding: 8px 8px 4px 8px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | |
| | | background: -ms-linear-gradient(left, #fbfbfb 0, #e9e9e9 100%); |
| | | background: linear-gradient(left, #fbfbfb 0, #e9e9e9 100%); |
| | | border-right: 1px solid #dfdfdf; |
| | | border-radius: 3px 0 0 0; /* for Opera */ |
| | | } |
| | | |
| | | #previewheaderstoggle .iconlink { |
| | |
| | | |
| | | #previewheaderstoggle.remove .iconlink { |
| | | top: auto; |
| | | bottom: 5px; |
| | | bottom: 15px; |
| | | background-position: -5px -242px; |
| | | } |
| | | |
| | | div.more-headers { |
| | | cursor: pointer; |
| | | height: 10px; |
| | | background: url(images/buttons.png) center -1619px no-repeat; |
| | | #previewheaderstoggle .iconlink.allheaders { |
| | | display: none; |
| | | } |
| | | |
| | | div.hide-headers { |
| | | background-position: center -1629px; |
| | | #previewheaderstoggle.remove .iconlink.allheaders { |
| | | top: auto; |
| | | bottom: 2px; |
| | | display: inline-block; |
| | | background-position: -27px -242px; |
| | | } |
| | | |
| | | #all-headers { |
| | | position: relative; |
| | | margin: 0 10px; |
| | | margin: 2px 0; |
| | | padding: 0; |
| | | height: 180px; |
| | | border: 1px solid #bbb; |
| | | background-color: #f0f0f0; |
| | | overflow: hidden; |
| | | border-radius: 4px; |
| | | background: #fff; |
| | | } |
| | | |
| | | #headers-source { |
| | |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | padding: 2px 5px; |
| | | padding: 2px; |
| | | overflow: auto; |
| | | text-align: left; |
| | | color: #333; |
| | | color: #666; |
| | | } |
| | | |
| | | #messagepreviewheader { |
| | | #messageheader { |
| | | position: relative; |
| | | height: auto; |
| | | margin: 0 8px 0 0; |
| | | padding: 0 0 6px 72px; |
| | | padding: 0 0 0 72px; |
| | | border-bottom: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | #messagepreviewheader h3.subject { |
| | | #messagecontent #messageheader { |
| | | padding: 0 0 0 90px; |
| | | min-height: 68px; |
| | | } |
| | | |
| | | #messageheader h3.subject { |
| | | padding: 8px 8px 2px 0; |
| | | } |
| | | |
| | | #messagepreviewheader #contactphoto { |
| | | #messageheader #contactphoto { |
| | | display: block; |
| | | position: absolute; |
| | | top: 11px; |
| | |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | #messagepreviewheader #contactphoto img { |
| | | #messageheader #contactphoto img { |
| | | width: 32px; |
| | | height: auto; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | #messageheader #contactphoto { |
| | | display: block; |
| | | position: absolute; |
| | | top: 40px; |
| | | right: 10px; |
| | | #messagecontent #messageheader #contactphoto { |
| | | top: 11px; |
| | | left: 31px; |
| | | width: 48px; |
| | | height: 48px; |
| | | overflow: hidden; |
| | | background: url(images/contactpic_48px.png) center center no-repeat #fff; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | #messageheader #contactphoto img { |
| | | #messagecontent #messageheader #contactphoto img { |
| | | width: 48px; |
| | | height: auto; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | #messagepreviewheader #countcontrols, |
| | | #messageheader #countcontrols { |
| | | position: absolute; |
| | | top: 8px; |
| | | right: 8px; |
| | | width: 20em; |
| | | right: 0; |
| | | text-align: right; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #messageheader .pagenav .countdisplay { |
| | | min-width: 0; |
| | | padding-right: 0.5em; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | #messagecontent .leftcol, |
| | | #messagepreview .leftcol { |
| | | margin-right: 252px; |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | #messagecontent .rightcol, |
| | | #messagepreview .rightcol { |
| | | float: right; |
| | | /* |
| | |
| | | min-height: 200px; |
| | | background: #f0f0f0; |
| | | padding: 8px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | #messagebody { |
| | |
| | | .uibox { |
| | | border: 1px solid #a3a3a3; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | box-shadow: 0 0 2px #999; |
| | | -o-box-shadow: 0 0 2px #999; |
| | | -webkit-box-shadow: 0 0 2px #999; |
| | |
| | | left: 0; |
| | | bottom: 0; |
| | | width: 100%; |
| | | min-width: 1150px; |
| | | min-width: 1024px; |
| | | } |
| | | |
| | | .scroller { |
| | |
| | | left: 0; |
| | | width: 100%; |
| | | bottom: 0; |
| | | overflow: auto; |
| | | overflow-x: hidden; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .listbox .scroller.withfooter { |
| | |
| | | * See http://creativecommons.org/licenses/by-sa/3.0/ for details. |
| | | */ |
| | | |
| | | ini_set('error_reporting', E_ALL &~ (E_NOTICE | E_STRICT)); |
| | | |
| | | header('Content-Type: image/svg+xml'); |
| | | header("Expires: ".gmdate("D, d M Y H:i:s", time()+864000)." GMT"); |
| | | header("Cache-Control: max-age=864000"); |
| | |
| | | background-image: url(svggradient.php?c=005d76;004558); |
| | | } |
| | | |
| | | #messageheader, #partheader, #composeheaders { |
| | | #partheader, #composeheaders { |
| | | background-image: url(svggradient.php?c=ffffff;e9e9e9); |
| | | } |
| | | |
| | |
| | | </span> |
| | | <roundcube:endif /> |
| | | <span class="composeoption"> |
| | | <label><label for="rcmcomposepriority"><roundcube:label name="priority" /> |
| | | <label for="rcmcomposepriority"><roundcube:label name="priority" /> |
| | | <roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" /></label> |
| | | </span> |
| | | <span class="composeoption"> |
| | |
| | | |
| | | <!-- folders list --> |
| | | <div id="mailboxcontainer" class="uibox listbox"> |
| | | <div class="scroller"> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" /> |
| | | </div> |
| | | <div class="scroller"> |
| | | <roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" /> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div><!-- end mailview-left --> |
| | | |
| | | <div id="mailview-right"> |
| | | <div id="mailview-right" class="uibox" style="top: 42px"> |
| | | |
| | | <div id="mailview-top"> |
| | | <div id="messageheader" class="uibox"> |
| | | <h2 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h2> |
| | | <roundcube:object name="messageHeaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject" /> |
| | | <roundcube:object name="messageFullHeaders" id="full-headers" /> |
| | | <div id="messagecontent"> |
| | | |
| | | <div id="messageheader"> |
| | | <h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3> |
| | | |
| | | <a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></span></a> |
| | | |
| | | <div id="contactphoto"><roundcube:object name="contactphoto" /></div> |
| | | |
| | | <table class="headers-table" id="preview-shortheaders"><tbody><tr> |
| | | <roundcube:if condition="env:mailbox == config:drafts_mbox || env:mailbox == config:sent_mbox"> |
| | | <td class="header-title"><roundcube:label name="to" /></td> |
| | | <td class="header from"><roundcube:object name="messageHeaders" valueOf="to" addicon="/images/addcontact.png" /></td> |
| | | <roundcube:else /> |
| | | <td class="header-title"><roundcube:label name="from" /></td> |
| | | <td class="header from"><roundcube:object name="messageHeaders" valueOf="from" addicon="/images/addcontact.png" /></td> |
| | | <roundcube:endif /> |
| | | <td class="header-title"><roundcube:label name="date" /></td> |
| | | <td class="header from"><roundcube:object name="messageHeaders" valueOf="date" /></td> |
| | | </tr></tbody></table> |
| | | |
| | | <roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" /> |
| | | <roundcube:object name="messageFullHeaders" no-switch="true" /> |
| | | |
| | | <!-- record navigation --> |
| | | <div id="countcontrols" class="pagenav"> |
| | |
| | | <roundcube:button command="nextmessage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextmessage" content="&gt;" /> |
| | | </div> |
| | | |
| | | <div id="contactphoto"><roundcube:object name="contactphoto" /></div> |
| | | </div><!-- end messageheader --> |
| | | |
| | | <div id="messagepreview"> |
| | | <div class="rightcol"> |
| | | <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" /> |
| | | </div> |
| | | <div class="leftcol"> |
| | | <roundcube:object name="messageObjects" id="message-objects" /> |
| | | <roundcube:object name="messageBody" id="messagebody" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div id="messagecontent" class="uibox"> |
| | | <div class="rightcol"> |
| | | <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" /> |
| | | </div> |
| | | <div class="leftcol"> |
| | | <roundcube:object name="messageObjects" id="message-objects" /> |
| | | <roundcube:object name="messageBody" id="messagebody" /> |
| | | </div> |
| | | </div> |
| | | </div><!-- end messagecontent --> |
| | | |
| | | </div><!-- end mailview-top --> |
| | | |
| | | <div id="mailview-bottom" class="uibox"> |
| | | <roundcube:object name="message" id="message" class="statusbar" /> |
| | | </div> |
| | | |
| | | </div><!-- end mailview-right --> |
| | | |
| | |
| | | |
| | | </div> |
| | | |
| | | <div id="mailview-right"> |
| | | |
| | | <!-- toolbar --> |
| | | <div id="messagetoolbar" class="fullwidth"> |
| | | <div id="mailtoolbar" class="toolbar"> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <div id="mailview-top" class="uibox watermark"></div> |
| | | <div id="mailview-right" class="uibox" style="top: 42px"> |
| | | |
| | | <div id="mailview-bottom" class="uibox"> |
| | | <roundcube:object name="message" id="message" class="statusbar" /> |
| | | </div> |
| | | <div id="messagecontent" class="watermark"></div> |
| | | |
| | | <roundcube:object name="message" id="message" class="statusbar" /> |
| | | |
| | | </div><!-- end mailview-right --> |
| | | |
| | |
| | | </head> |
| | | <body class="iframe fullheight"> |
| | | |
| | | <div id="messagepreviewheader"> |
| | | <div id="messageheader"> |
| | | <h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3> |
| | | |
| | | <a href="#details" id="previewheaderstoggle"><span class="iconlink"></span></a> |
| | | <a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></a> |
| | | <div id="contactphoto"><roundcube:object name="contactphoto" /></div> |
| | | |
| | | <table class="headers-table" id="preview-shortheaders"><tbody><tr> |
| | |
| | | </tr></tbody></table> |
| | | |
| | | <roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" /> |
| | | <roundcube:object name="messageFullHeaders" no-switch="true" /> |
| | | |
| | | <!-- record navigation --> |
| | | <div id="countcontrols" class="pagenav"> |
| | |
| | | |
| | | if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') { |
| | | layout_messageview(); |
| | | rcmail.addEventListener('aftershow-headers', function() { layout_messageview(); }); |
| | | rcmail.addEventListener('afterhide-headers', function() { layout_messageview(); }); |
| | | $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false }); |
| | | $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false; }); |
| | | $('#headerstoggleall').click(function(e){ toggle_all_headers(this); return false; }); |
| | | } |
| | | else if (rcmail.env.action == 'compose') { |
| | | rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); }); |
| | |
| | | |
| | | new rcube_scroller('#directorylist-content', '#directorylist-header', '#directorylist-footer'); |
| | | } |
| | | } |
| | | |
| | | // set min-width to show all toolbar buttons |
| | | var screen = $('.minwidth'); |
| | | if (screen.length) { |
| | | screen.css('min-width', $('.toolbar').width() + $('#quicksearchbar').parent().width() + 20); |
| | | } |
| | | |
| | | // turn a group of fieldsets into tabs |
| | |
| | | */ |
| | | function resize() |
| | | { |
| | | if (rcmail.env.task == 'mail' && (rcmail.env.action == 'show' || rcmail.env.action == 'preview')) { |
| | | layout_messageview(); |
| | | } |
| | | if (rcmail.env.task == 'mail' && rcmail.env.action == 'compose') { |
| | | layout_composeview(); |
| | | if (rcmail.env.task == 'mail') { |
| | | if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') |
| | | layout_messageview(); |
| | | else if (rcmail.env.action == 'compose') |
| | | layout_composeview(); |
| | | } |
| | | |
| | | // make iframe footer buttons float if scrolling is active |
| | |
| | | body = $(document.body), |
| | | floating = footer.hasClass('floating'), |
| | | overflow = body.outerHeight(true) > $(window).height(); |
| | | |
| | | if (overflow != floating) { |
| | | var action = overflow ? 'addClass' : 'removeClass'; |
| | | footer[action]('floating'); |
| | | body[action]('floatingbuttons'); |
| | | } |
| | | }) |
| | | |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | function layout_messageview() |
| | | { |
| | | $('#messagecontent').css('top', ($('#messageheader').outerHeight() + 10) + 'px'); |
| | | $('#message-objects div a').addClass('button'); |
| | | |
| | | if (!$('#attachment-list li').length) { |
| | |
| | | var button = $(e.target), |
| | | frame = $('#mailpreviewframe'), |
| | | visible = !frame.is(':visible'), |
| | | splitter = mailviewsplit.pos || parseInt(bw.get_cookie('mailviewsplitter') || 320), |
| | | splitter = mailviewsplit.pos || parseInt(rcmail.get_cookie('mailviewsplitter') || 320), |
| | | topstyles, bottomstyles, uid; |
| | | |
| | | frame.toggle(); |
| | |
| | | { |
| | | $('#preview-shortheaders').toggle(); |
| | | var full = $('#preview-allheaders').toggle(), |
| | | button = $('a#previewheaderstoggle'); |
| | | button = $('#previewheaderstoggle'); |
| | | |
| | | if (!$('#headerstoggleall').length) |
| | | $('#all-headers').toggle(); |
| | | |
| | | // add toggle button to full headers table |
| | | if (full.is(':visible')) |
| | | button.attr('href', '#hide').removeClass('add').addClass('remove') |
| | | else |
| | | button.attr('href', '#details').removeClass('remove').addClass('add') |
| | | if (full.is(':visible')) { |
| | | button.attr('href', '#hide').removeClass('add').addClass('remove'); |
| | | } |
| | | else { |
| | | button.attr('href', '#details').removeClass('remove').addClass('add'); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Show/hide all message headers |
| | | */ |
| | | function toggle_all_headers(button) |
| | | { |
| | | rcmail.command('show-headers', '', button); |
| | | $(button).remove(); |
| | | $('#previewheaderstoggle span').css({bottom: '5px'}); |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | |
| | | // Select/unselect tab |
| | | $('#tab'+idx).toggleClass('selected', idx==index); |
| | | }); |
| | | |
| | | resize(); |
| | | } |
| | | |
| | | /** |
| | |
| | | $(window).resize(onResize); |
| | | |
| | | // read saved position from cookie |
| | | var cookie = bw.get_cookie(this.id); |
| | | var cookie = rcmail.get_cookie(this.id); |
| | | if (cookie && !isNaN(cookie)) { |
| | | this.pos = parseFloat(cookie); |
| | | this.resize(); |
| | |
| | | { |
| | | var exp = new Date(); |
| | | exp.setYear(exp.getFullYear() + 1); |
| | | bw.set_cookie(this.id, this.pos, exp); |
| | | rcmail.set_cookie(this.id, this.pos, exp); |
| | | }; |
| | | |
| | | } // end class rcube_splitter |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_base_replacer class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_BaseReplacer extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_base_replacer('test'); |
| | | |
| | | $this->assertInstanceOf('rcube_base_replacer', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_browser class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Browser extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_browser(); |
| | | |
| | | $this->assertInstanceOf('rcube_browser', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_cache class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Cache extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_cache('db', 1); |
| | | |
| | | $this->assertInstanceOf('rcube_cache', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_charset class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Charset extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Data for test_clean() |
| | | */ |
| | | function data_clean() |
| | | { |
| | | return array( |
| | | array('', '', 'Empty string'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_clean |
| | | */ |
| | | function test_clean($input, $output, $title) |
| | | { |
| | | $this->assertEquals(rcube_charset::clean($input), $output, $title); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_content_filter class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_ContentFilter extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_content_filter(); |
| | | |
| | | $this->assertInstanceOf('rcube_content_filter', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_html class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Html extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new html; |
| | | |
| | | $this->assertInstanceOf('html', $object, "Class constructor"); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_quote() |
| | | */ |
| | | function data_quote() |
| | | { |
| | | return array( |
| | | array('abc', 'abc'), |
| | | array('?', '?'), |
| | | array('"', '"'), |
| | | array('<', '<'), |
| | | array('>', '>'), |
| | | array('&', '&'), |
| | | array('&', '&amp;'), |
| | | array('&', '&', true), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Test for quote() |
| | | * @dataProvider data_quote |
| | | */ |
| | | function test_quote($str, $result, $validate = false) |
| | | { |
| | | $this->assertEquals(html::quote($str, $validate), $result); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_image class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Image extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_image('test'); |
| | | |
| | | $this->assertInstanceOf('rcube_image', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_imap class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Imap extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_imap; |
| | | |
| | | $this->assertInstanceOf('rcube_imap', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_imap_generic class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_ImapGeneric extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_imap_generic; |
| | | |
| | | $this->assertInstanceOf('rcube_imap_generic', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_message_header class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_MessageHeader extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_message_header; |
| | | |
| | | $this->assertInstanceOf('rcube_message_header', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_message_part class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_MessagePart extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_message_part; |
| | | |
| | | $this->assertInstanceOf('rcube_message_part', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_mime class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Mime extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Test decoding of single e-mail address strings |
| | | * Uses rcube_mime::decode_address_list() |
| | | */ |
| | | function test_decode_single_address() |
| | | { |
| | | $headers = array( |
| | | 0 => 'test@domain.tld', |
| | | 1 => '<test@domain.tld>', |
| | | 2 => 'Test <test@domain.tld>', |
| | | 3 => 'Test Test <test@domain.tld>', |
| | | 4 => 'Test Test<test@domain.tld>', |
| | | 5 => '"Test Test" <test@domain.tld>', |
| | | 6 => '"Test Test"<test@domain.tld>', |
| | | 7 => '"Test \\" Test" <test@domain.tld>', |
| | | 8 => '"Test<Test" <test@domain.tld>', |
| | | 9 => '=?ISO-8859-1?B?VGVzdAo=?= <test@domain.tld>', |
| | | 10 => '=?ISO-8859-1?B?VGVzdAo=?=<test@domain.tld>', // #1487068 |
| | | // comments in address (#1487673) |
| | | 11 => 'Test (comment) <test@domain.tld>', |
| | | 12 => '"Test" (comment) <test@domain.tld>', |
| | | 13 => '"Test (comment)" (comment) <test@domain.tld>', |
| | | 14 => '(comment) <test@domain.tld>', |
| | | 15 => 'Test <test@(comment)domain.tld>', |
| | | 16 => 'Test Test ((comment)) <test@domain.tld>', |
| | | 17 => 'test@domain.tld (comment)', |
| | | 18 => '"Test,Test" <test@domain.tld>', |
| | | // 1487939 |
| | | 19 => 'Test <"test test"@domain.tld>', |
| | | 20 => '<"test test"@domain.tld>', |
| | | 21 => '"test test"@domain.tld', |
| | | ); |
| | | |
| | | $results = array( |
| | | 0 => array(1, '', 'test@domain.tld'), |
| | | 1 => array(1, '', 'test@domain.tld'), |
| | | 2 => array(1, 'Test', 'test@domain.tld'), |
| | | 3 => array(1, 'Test Test', 'test@domain.tld'), |
| | | 4 => array(1, 'Test Test', 'test@domain.tld'), |
| | | 5 => array(1, 'Test Test', 'test@domain.tld'), |
| | | 6 => array(1, 'Test Test', 'test@domain.tld'), |
| | | 7 => array(1, 'Test " Test', 'test@domain.tld'), |
| | | 8 => array(1, 'Test<Test', 'test@domain.tld'), |
| | | 9 => array(1, 'Test', 'test@domain.tld'), |
| | | 10 => array(1, 'Test', 'test@domain.tld'), |
| | | 11 => array(1, 'Test', 'test@domain.tld'), |
| | | 12 => array(1, 'Test', 'test@domain.tld'), |
| | | 13 => array(1, 'Test (comment)', 'test@domain.tld'), |
| | | 14 => array(1, '', 'test@domain.tld'), |
| | | 15 => array(1, 'Test', 'test@domain.tld'), |
| | | 16 => array(1, 'Test Test', 'test@domain.tld'), |
| | | 17 => array(1, '', 'test@domain.tld'), |
| | | 18 => array(1, 'Test,Test', 'test@domain.tld'), |
| | | 19 => array(1, 'Test', '"test test"@domain.tld'), |
| | | 20 => array(1, '', '"test test"@domain.tld'), |
| | | 21 => array(1, '', '"test test"@domain.tld'), |
| | | ); |
| | | |
| | | foreach ($headers as $idx => $header) { |
| | | $res = rcube_mime::decode_address_list($header); |
| | | |
| | | $this->assertEquals($results[$idx][0], count($res), "Rows number in result for header: " . $header); |
| | | $this->assertEquals($results[$idx][1], $res[1]['name'], "Name part decoding for header: " . $header); |
| | | $this->assertEquals($results[$idx][2], $res[1]['mailto'], "Email part decoding for header: " . $header); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Test decoding of header values |
| | | * Uses rcube_mime::decode_mime_string() |
| | | */ |
| | | function test_header_decode_qp() |
| | | { |
| | | $test = array( |
| | | // #1488232: invalid character "?" |
| | | 'quoted-printable (1)' => array( |
| | | 'in' => '=?utf-8?Q?Certifica=C3=A7=C3=A3??=', |
| | | 'out' => 'Certifica=C3=A7=C3=A3?', |
| | | ), |
| | | 'quoted-printable (2)' => array( |
| | | 'in' => '=?utf-8?Q?Certifica=?= =?utf-8?Q?C3=A7=C3=A3?=', |
| | | 'out' => 'Certifica=C3=A7=C3=A3', |
| | | ), |
| | | 'quoted-printable (3)' => array( |
| | | 'in' => '=?utf-8?Q??= =?utf-8?Q??=', |
| | | 'out' => '', |
| | | ), |
| | | 'quoted-printable (4)' => array( |
| | | 'in' => '=?utf-8?Q??= a =?utf-8?Q??=', |
| | | 'out' => ' a ', |
| | | ), |
| | | 'quoted-printable (5)' => array( |
| | | 'in' => '=?utf-8?Q?a?= =?utf-8?Q?b?=', |
| | | 'out' => 'ab', |
| | | ), |
| | | 'quoted-printable (6)' => array( |
| | | 'in' => '=?utf-8?Q? ?= =?utf-8?Q?a?=', |
| | | 'out' => ' a', |
| | | ), |
| | | 'quoted-printable (7)' => array( |
| | | 'in' => '=?utf-8?Q?___?= =?utf-8?Q?a?=', |
| | | 'out' => ' a', |
| | | ), |
| | | ); |
| | | |
| | | foreach ($test as $idx => $item) { |
| | | $res = rcube_mime::decode_mime_string($item['in'], 'UTF-8'); |
| | | $res = quoted_printable_encode($res); |
| | | |
| | | $this->assertEquals($item['out'], $res, "Header decoding for: " . $idx); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Rcube extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = rcube::get_instance(); |
| | | |
| | | $this->assertInstanceOf('rcube', $object, "Class singleton"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_result_index class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_ResultIndex extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_result_index; |
| | | |
| | | $this->assertInstanceOf('rcube_result_index', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_result_set class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_ResultSet extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_result_set; |
| | | |
| | | $this->assertInstanceOf('rcube_result_set', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_result_thread class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_ResultThread extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_result_thread; |
| | | |
| | | $this->assertInstanceOf('rcube_result_thread', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_shared functions |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Shared extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * rcube_shared.inc: in_array_nocase() |
| | | */ |
| | | function test_in_array_nocase() |
| | | { |
| | | $haystack = array('Test'); |
| | | $needle = 'test'; |
| | | $result = in_array_nocase($needle, $haystack); |
| | | |
| | | $this->assertTrue($result, $title); |
| | | |
| | | $result = in_array_nocase($needle, null); |
| | | |
| | | $this->assertFalse($result, $title); |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: get_boolean() |
| | | */ |
| | | function test_get_boolean() |
| | | { |
| | | $input = array( |
| | | false, 'false', '0', 'no', 'off', 'nein', 'FALSE', '', null, |
| | | ); |
| | | |
| | | foreach ($input as $idx => $value) { |
| | | $this->assertFalse(get_boolean($value), "Invalid result for $idx test item"); |
| | | } |
| | | |
| | | $input = array( |
| | | true, 'true', '1', 1, 'yes', 'anything', 1000, |
| | | ); |
| | | |
| | | foreach ($input as $idx => $value) { |
| | | $this->assertTrue(get_boolean($value), "Invalid result for $idx test item"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: parse_bytes() |
| | | */ |
| | | function test_parse_bytes() |
| | | { |
| | | $data = array( |
| | | '1' => 1, |
| | | '1024' => 1024, |
| | | '2k' => 2 * 1024, |
| | | '2 k' => 2 * 1024, |
| | | '2kb' => 2 * 1024, |
| | | '2kB' => 2 * 1024, |
| | | '2m' => 2 * 1048576, |
| | | '2 m' => 2 * 1048576, |
| | | '2mb' => 2 * 1048576, |
| | | '2mB' => 2 * 1048576, |
| | | '2g' => 2 * 1024 * 1048576, |
| | | '2 g' => 2 * 1024 * 1048576, |
| | | '2gb' => 2 * 1024 * 1048576, |
| | | '2gB' => 2 * 1024 * 1048576, |
| | | ); |
| | | |
| | | foreach ($data as $value => $expected) { |
| | | $result = parse_bytes($value); |
| | | $this->assertEquals($expected, $result, "Invalid parse_bytes() result for $value"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: slashify() |
| | | */ |
| | | function test_slashify() |
| | | { |
| | | $data = array( |
| | | 'test' => 'test/', |
| | | 'test/' => 'test/', |
| | | '' => '/', |
| | | "\\" => "\\/", |
| | | ); |
| | | |
| | | foreach ($data as $value => $expected) { |
| | | $result = slashify($value); |
| | | $this->assertEquals($expected, $result, "Invalid slashify() result for $value"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: unslashify() |
| | | */ |
| | | function test_unslashify() |
| | | { |
| | | $data = array( |
| | | 'test' => 'test', |
| | | 'test/' => 'test', |
| | | '/' => '', |
| | | "\\/" => "\\", |
| | | 'test/test' => 'test/test', |
| | | 'test//' => 'test', |
| | | ); |
| | | |
| | | foreach ($data as $value => $expected) { |
| | | $result = unslashify($value); |
| | | $this->assertEquals($expected, $result, "Invalid unslashify() result for $value"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: get_offset_sec() |
| | | */ |
| | | function test_get_offset_sec() |
| | | { |
| | | $data = array( |
| | | '1s' => 1, |
| | | '1m' => 1 * 60, |
| | | '1h' => 1 * 60 * 60, |
| | | '1d' => 1 * 60 * 60 * 24, |
| | | '1w' => 1 * 60 * 60 * 24 * 7, |
| | | '1y' => (int) '1y', |
| | | 100 => 100, |
| | | '100' => 100, |
| | | ); |
| | | |
| | | foreach ($data as $value => $expected) { |
| | | $result = get_offset_sec($value); |
| | | $this->assertEquals($expected, $result, "Invalid get_offset_sec() result for $value"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: array_keys_recursive() |
| | | */ |
| | | function test_array_keys_recursive() |
| | | { |
| | | $input = array( |
| | | 'one' => array( |
| | | 'two' => array( |
| | | 'three' => array(), |
| | | 'four' => 'something', |
| | | ), |
| | | ), |
| | | 'five' => 'test', |
| | | ); |
| | | |
| | | $result = array_keys_recursive($input); |
| | | $input_str = 'one,two,three,four,five'; |
| | | $result_str = implode(',', $result); |
| | | |
| | | $this->assertEquals($input_str, $result_str, "Invalid array_keys_recursive() result"); |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: format_email() |
| | | */ |
| | | function test_format_email() |
| | | { |
| | | $data = array( |
| | | '' => '', |
| | | 'test' => 'test', |
| | | 'test@test.tld' => 'test@test.tld', |
| | | 'test@[127.0.0.1]' => 'test@[127.0.0.1]', |
| | | 'TEST@TEST.TLD' => 'TEST@test.tld', |
| | | ); |
| | | |
| | | foreach ($data as $value => $expected) { |
| | | $result = format_email($value); |
| | | $this->assertEquals($expected, $result, "Invalid format_email() result for $value"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * rcube_shared.inc: format_email_recipient() |
| | | */ |
| | | function test_format_email_recipient() |
| | | { |
| | | $data = array( |
| | | '' => array(''), |
| | | 'test' => array('test'), |
| | | 'test@test.tld' => array('test@test.tld'), |
| | | 'test@[127.0.0.1]' => array('test@[127.0.0.1]'), |
| | | 'TEST@TEST.TLD' => array('TEST@TEST.TLD'), |
| | | 'TEST <test@test.tld>' => array('test@test.tld', 'TEST'), |
| | | '"TEST\"" <test@test.tld>' => array('test@test.tld', 'TEST"'), |
| | | ); |
| | | |
| | | foreach ($data as $expected => $value) { |
| | | $result = format_email_recipient($value[0], $value[1]); |
| | | $this->assertEquals($expected, $result, "Invalid format_email_recipient()"); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_smtp class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Smtp extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_smtp; |
| | | |
| | | $this->assertInstanceOf('rcube_smtp', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_spellchecker class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Spellchecker extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $object = new rcube_spellchecker; |
| | | |
| | | $this->assertInstanceOf('rcube_spellchecker', $object, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_string_replacer class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_StringReplacer extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $sr = new rcube_string_replacer; |
| | | |
| | | $this->assertInstanceOf('rcube_string_replacer', $sr, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_user class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_User extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Class constructor |
| | | */ |
| | | function test_class() |
| | | { |
| | | $user = new rcube_user; |
| | | |
| | | $this->assertInstanceOf('rcube_user', $user, "Class constructor"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test rcube_utils class |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_Utils extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | /** |
| | | * Valid email addresses for test_valid_email() |
| | | */ |
| | | function data_valid_email() |
| | | { |
| | | return array( |
| | | array('email@domain.com', 'Valid email'), |
| | | array('firstname.lastname@domain.com', 'Email contains dot in the address field'), |
| | | array('email@subdomain.domain.com', 'Email contains dot with subdomain'), |
| | | array('firstname+lastname@domain.com', 'Plus sign is considered valid character'), |
| | | array('email@[123.123.123.123]', 'Square bracket around IP address'), |
| | | array('email@[IPv6:::1]', 'Square bracket around IPv6 address (1)'), |
| | | array('email@[IPv6:::1.2.3.4]', 'Square bracket around IPv6 address (2)'), |
| | | array('email@[IPv6:2001:2d12:c4fe:5afe::1]', 'Square bracket around IPv6 address (3)'), |
| | | array('"email"@domain.com', 'Quotes around email is considered valid'), |
| | | array('1234567890@domain.com', 'Digits in address are valid'), |
| | | array('email@domain-one.com', 'Dash in domain name is valid'), |
| | | array('_______@domain.com', 'Underscore in the address field is valid'), |
| | | array('email@domain.name', '.name is valid Top Level Domain name'), |
| | | array('email@domain.co.jp', 'Dot in Top Level Domain name also considered valid (use co.jp as example here)'), |
| | | array('firstname-lastname@domain.com', 'Dash in address field is valid'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Invalid email addresses for test_invalid_email() |
| | | */ |
| | | function data_invalid_email() |
| | | { |
| | | return array( |
| | | array('plainaddress', 'Missing @ sign and domain'), |
| | | array('#@%^%#$@#$@#.com', 'Garbage'), |
| | | array('@domain.com', 'Missing username'), |
| | | array('Joe Smith <email@domain.com>', 'Encoded html within email is invalid'), |
| | | array('email.domain.com', 'Missing @'), |
| | | array('email@domain@domain.com', 'Two @ sign'), |
| | | array('.email@domain.com', 'Leading dot in address is not allowed'), |
| | | array('email.@domain.com', 'Trailing dot in address is not allowed'), |
| | | array('email..email@domain.com', 'Multiple dots'), |
| | | array('あいうえお@domain.com', 'Unicode char as address'), |
| | | array('email@domain.com (Joe Smith)', 'Text followed email is not allowed'), |
| | | array('email@domain', 'Missing top level domain (.com/.net/.org/etc)'), |
| | | array('email@-domain.com', 'Leading dash in front of domain is invalid'), |
| | | // array('email@domain.web', '.web is not a valid top level domain'), |
| | | array('email@123.123.123.123', 'IP address without brackets'), |
| | | array('email@2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets'), |
| | | array('email@IPv6:2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets (2)'), |
| | | array('email@[111.222.333.44444]', 'Invalid IP format'), |
| | | array('email@[111.222.255.257]', 'Invalid IP format (2)'), |
| | | array('email@[.222.255.257]', 'Invalid IP format (3)'), |
| | | array('email@[::1]', 'Invalid IPv6 format (1)'), |
| | | array('email@[IPv6:2001:23x2:1]', 'Invalid IPv6 format (2)'), |
| | | array('email@[IPv6:1111:2222:33333::4444:5555]', 'Invalid IPv6 format (3)'), |
| | | array('email@[IPv6:1111::3333::4444:5555]', 'Invalid IPv6 format (4)'), |
| | | array('email@domain..com', 'Multiple dot in the domain portion is invalid'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_valid_email |
| | | */ |
| | | function test_valid_email($email, $title) |
| | | { |
| | | $this->assertTrue(rcube_utils::check_email($email, false), $title); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_invalid_email |
| | | */ |
| | | function test_invalid_email($email, $title) |
| | | { |
| | | $this->assertFalse(rcube_utils::check_email($email, false), $title); |
| | | } |
| | | |
| | | /** |
| | | * Valid IP addresses for test_valid_ip() |
| | | */ |
| | | function data_valid_ip() |
| | | { |
| | | return array( |
| | | array('0.0.0.0'), |
| | | array('123.123.123.123'), |
| | | array('::'), |
| | | array('::1'), |
| | | array('::1.2.3.4'), |
| | | array('2001:2d12:c4fe:5afe::1'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Valid IP addresses for test_invalid_ip() |
| | | */ |
| | | function data_invalid_ip() |
| | | { |
| | | return array( |
| | | array(''), |
| | | array(0), |
| | | array('123.123.123.1234'), |
| | | array('1.1.1.1.1'), |
| | | array('::1.2.3.260'), |
| | | array('::1.0'), |
| | | array('2001::c4fe:5afe::1'), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_valid_ip |
| | | */ |
| | | function test_valid_ip($ip) |
| | | { |
| | | $this->assertTrue(rcube_utils::check_ip($ip)); |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_invalid_ip |
| | | */ |
| | | function test_invalid_ip($ip) |
| | | { |
| | | $this->assertFalse(rcube_utils::check_ip($ip)); |
| | | } |
| | | |
| | | /** |
| | | * Data for test_rep_specialchars_output() |
| | | */ |
| | | function data_rep_specialchars_output() |
| | | { |
| | | return array( |
| | | array('', '', 'abc', 'abc'), |
| | | array('', '', '?', '?'), |
| | | array('', '', '"', '"'), |
| | | array('', '', '<', '<'), |
| | | array('', '', '>', '>'), |
| | | array('', '', '&', '&'), |
| | | array('', '', '&', '&amp;'), |
| | | array('', '', '<a>', '<a>'), |
| | | array('', 'remove', '<a>', ''), |
| | | ); |
| | | } |
| | | |
| | | /** |
| | | * Test for rep_specialchars_output |
| | | * @dataProvider data_rep_specialchars_output |
| | | */ |
| | | function test_rep_specialchars_output($type, $mode, $str, $res) |
| | | { |
| | | $result = rcube_utils::rep_specialchars_output( |
| | | $str, $type ? $type : 'html', $mode ? $mode : 'strict'); |
| | | |
| | | $this->assertEquals($result, $res); |
| | | } |
| | | |
| | | /** |
| | | * rcube_utils::mod_css_styles() |
| | | */ |
| | | function test_mod_css_styles() |
| | | { |
| | | $css = file_get_contents(TESTS_DIR . 'src/valid.css'); |
| | | $mod = rcube_utils::mod_css_styles($css, 'rcmbody'); |
| | | |
| | | $this->assertRegExp('/#rcmbody\s+\{/', $mod, "Replace body style definition"); |
| | | $this->assertRegExp('/#rcmbody h1\s\{/', $mod, "Prefix tag styles (single)"); |
| | | $this->assertRegExp('/#rcmbody h1, #rcmbody h2, #rcmbody h3, #rcmbody textarea\s+\{/', $mod, "Prefix tag styles (multiple)"); |
| | | $this->assertRegExp('/#rcmbody \.noscript\s+\{/', $mod, "Prefix class styles"); |
| | | } |
| | | |
| | | /** |
| | | * rcube_utils::mod_css_styles() |
| | | */ |
| | | function test_mod_css_styles_xss() |
| | | { |
| | | $mod = rcube_utils::mod_css_styles("body.main2cols { background-image: url('../images/leftcol.png'); }", 'rcmbody'); |
| | | $this->assertEquals("/* evil! */", $mod, "No url() values allowed"); |
| | | |
| | | $mod = rcube_utils::mod_css_styles("@import url('http://localhost/somestuff/css/master.css');", 'rcmbody'); |
| | | $this->assertEquals("/* evil! */", $mod, "No import statements"); |
| | | |
| | | $mod = rcube_utils::mod_css_styles("left:expression(document.body.offsetWidth-20)", 'rcmbody'); |
| | | $this->assertEquals("/* evil! */", $mod, "No expression properties"); |
| | | |
| | | $mod = rcube_utils::mod_css_styles("left:exp/* */ression( alert('xss3') )", 'rcmbody'); |
| | | $this->assertEquals("/* evil! */", $mod, "Don't allow encoding quirks"); |
| | | |
| | | $mod = rcube_utils::mod_css_styles("background:\\0075\\0072\\006c( javascript:alert('xss') )", 'rcmbody'); |
| | | $this->assertEquals("/* evil! */", $mod, "Don't allow encoding quirks (2)"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Unit tests for class rcube_vcard |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class Framework_VCard extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | function _srcpath($fn) |
| | | { |
| | | return realpath(dirname(__FILE__) . '/../src/' . $fn); |
| | | } |
| | | |
| | | function test_parse_one() |
| | | { |
| | | $vcard = new rcube_vcard(file_get_contents($this->_srcpath('apple.vcf'))); |
| | | |
| | | $this->assertTrue($vcard->business, "Identify as business record"); |
| | | $this->assertEquals("Apple Computer AG", $vcard->displayname, "FN => displayname"); |
| | | $this->assertEquals("", $vcard->firstname, "No person name set"); |
| | | } |
| | | |
| | | function test_parse_two() |
| | | { |
| | | $vcard = new rcube_vcard(file_get_contents($this->_srcpath('johndoe.vcf')), null); |
| | | |
| | | $this->assertFalse($vcard->business, "Identify as private record"); |
| | | $this->assertEquals("John Doë", $vcard->displayname, "Decode according to charset attribute"); |
| | | $this->assertEquals("roundcube.net", $vcard->organization, "Test organization field"); |
| | | $this->assertCount(2, $vcard->email, "List two e-mail addresses"); |
| | | $this->assertEquals("roundcube@gmail.com", $vcard->email[0], "Use PREF e-mail as primary"); |
| | | } |
| | | |
| | | function test_import() |
| | | { |
| | | $input = file_get_contents($this->_srcpath('apple.vcf')); |
| | | $input .= file_get_contents($this->_srcpath('johndoe.vcf')); |
| | | |
| | | $vcards = rcube_vcard::import($input); |
| | | |
| | | $this->assertCount(2, $vcards, "Detected 2 vcards"); |
| | | $this->assertEquals("Apple Computer AG", $vcards[0]->displayname, "FN => displayname"); |
| | | $this->assertEquals("John Doë", $vcards[1]->displayname, "Displayname with correct charset"); |
| | | |
| | | // http://trac.roundcube.net/ticket/1485542 |
| | | $vcards2 = rcube_vcard::import(file_get_contents($this->_srcpath('thebat.vcf'))); |
| | | $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')); |
| | | |
| | | $vcards = rcube_vcard::import($input); |
| | | $this->assertEquals("Ǽgean ĽdaMonté", $vcards[0]->displayname, "Decoded from UTF-16"); |
| | | } |
| | | } |
File was renamed from tests/html_to_text.php |
| | |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class rcube_test_html2text extends UnitTestCase |
| | | class HtmlToText extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | function __construct() |
| | | function data_html2text() |
| | | { |
| | | $this->UnitTestCase("HTML-to-Text conversion tests"); |
| | | |
| | | } |
| | | |
| | | function test_html2text() |
| | | { |
| | | $data = array( |
| | | return array( |
| | | 0 => array( |
| | | 'title' => 'Test entry', |
| | | 'in' => '', |
| | |
| | | 'out' => 'Ś', |
| | | ), |
| | | ); |
| | | |
| | | $ht = new html2text(null, false, false); |
| | | |
| | | foreach ($data as $idx => $item) { |
| | | $ht->set_html($item['in']); |
| | | $res = $ht->get_text(); |
| | | $this->assertEqual($item['out'], $res, $item['title'] . "($idx)"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @dataProvider data_html2text |
| | | */ |
| | | function test_html2text($title, $in, $out) |
| | | { |
| | | $ht = new html2text(null, false, false); |
| | | |
| | | $ht->set_html($in); |
| | | $res = $ht->get_text(); |
| | | |
| | | $this->assertEquals($out, $res, $title); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | * Test class to test steps/mail/func.inc functions |
| | | * |
| | | * @package Tests |
| | | */ |
| | | class MailFunc extends PHPUnit_Framework_TestCase |
| | | { |
| | | |
| | | function setUp() |
| | | { |
| | | // simulate environment to successfully include func.inc |
| | | $GLOBALS['RCMAIL'] = $RCMAIL = rcmail::get_instance(); |
| | | $GLOBALS['OUTPUT'] = $OUTPUT = $RCMAIL->load_gui(); |
| | | $RCMAIL->action = 'autocomplete'; |
| | | $RCMAIL->storage_init(false); |
| | | |
| | | require_once INSTALL_PATH . 'program/steps/mail/func.inc'; |
| | | |
| | | $GLOBALS['EMAIL_ADDRESS_PATTERN'] = $EMAIL_ADDRESS_PATTERN; |
| | | } |
| | | |
| | | /** |
| | | * Helper method to create a HTML message part object |
| | | */ |
| | | function get_html_part($body) |
| | | { |
| | | $part = new rcube_message_part; |
| | | $part->ctype_primary = 'text'; |
| | | $part->ctype_secondary = 'html'; |
| | | $part->body = file_get_contents(TESTS_DIR . $body); |
| | | $part->replaces = array(); |
| | | return $part; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Test sanitization of a "normal" html message |
| | | */ |
| | | function test_html() |
| | | { |
| | | $part = $this->get_html_part('src/htmlbody.txt'); |
| | | $part->replaces = array('ex1.jpg' => 'part_1.2.jpg', 'ex2.jpg' => 'part_1.2.jpg'); |
| | | |
| | | // render HTML in normal mode |
| | | $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo'); |
| | | |
| | | $this->assertRegExp('/src="'.$part->replaces['ex1.jpg'].'"/', $html, "Replace reference to inline image"); |
| | | $this->assertRegExp('#background="./program/resources/blocked.gif"#', $html, "Replace external background image"); |
| | | $this->assertNotRegExp('/ex3.jpg/', $html, "No references to external images"); |
| | | $this->assertNotRegExp('/<meta [^>]+>/', $html, "No meta tags allowed"); |
| | | //$this->assertNoPattern('/<style [^>]+>/', $html, "No style tags allowed"); |
| | | $this->assertNotRegExp('/<form [^>]+>/', $html, "No form tags allowed"); |
| | | $this->assertRegExp('/Subscription form/', $html, "Include <form> contents"); |
| | | $this->assertRegExp('/<!-- link ignored -->/', $html, "No external links allowed"); |
| | | $this->assertRegExp('/<a[^>]+ target="_blank">/', $html, "Set target to _blank"); |
| | | $this->assertTrue($GLOBALS['REMOTE_OBJECTS'], "Remote object detected"); |
| | | |
| | | // render HTML in safe mode |
| | | $html2 = rcmail_html4inline(rcmail_print_body($part, array('safe' => true)), 'foo'); |
| | | |
| | | $this->assertRegExp('/<style [^>]+>/', $html2, "Allow styles in safe mode"); |
| | | $this->assertRegExp('#src="http://evilsite.net/mailings/ex3.jpg"#', $html2, "Allow external images in HTML (safe mode)"); |
| | | $this->assertRegExp("#url\('?http://evilsite.net/newsletter/image/bg/bg-64.jpg'?\)#", $html2, "Allow external images in CSS (safe mode)"); |
| | | $css = '<link rel="stylesheet" .+_u=tmp-[a-z0-9]+\.css.+_action=modcss'; |
| | | $this->assertRegExp('#'.$css.'#Ui', $html2, "Filter (anonymized) external styleseehts with utils/modcss.inc"); |
| | | } |
| | | |
| | | /** |
| | | * Test the elimination of some trivial XSS vulnerabilities |
| | | */ |
| | | function test_html_xss() |
| | | { |
| | | $part = $this->get_html_part('src/htmlxss.txt'); |
| | | $washed = rcmail_print_body($part, array('safe' => true)); |
| | | |
| | | $this->assertNotRegExp('/src="skins/', $washed, "Remove local references"); |
| | | $this->assertNotRegExp('/\son[a-z]+/', $washed, "Remove on* attributes"); |
| | | |
| | | $html = rcmail_html4inline($washed, 'foo'); |
| | | $this->assertNotRegExp('/onclick="return rcmail.command(\'compose\',\'xss@somehost.net\',this)"/', $html, "Clean mailto links"); |
| | | $this->assertNotRegExp('/alert/', $html, "Remove alerts"); |
| | | } |
| | | |
| | | /** |
| | | * Test HTML sanitization to fix the CSS Expression Input Validation Vulnerability |
| | | * reported at http://www.securityfocus.com/bid/26800/ |
| | | */ |
| | | function test_html_xss2() |
| | | { |
| | | $part = $this->get_html_part('src/BID-26800.txt'); |
| | | $washed = rcmail_html4inline(rcmail_print_body($part, array('safe' => true)), 'dabody', '', $attr, true); |
| | | |
| | | $this->assertNotRegExp('/alert|expression|javascript|xss/', $washed, "Remove evil style blocks"); |
| | | $this->assertNotRegExp('/font-style:italic/', $washed, "Allow valid styles"); |
| | | } |
| | | |
| | | /** |
| | | * Test washtml class on non-unicode characters (#1487813) |
| | | */ |
| | | function test_washtml_utf8() |
| | | { |
| | | $part = $this->get_html_part('src/invalidchars.html'); |
| | | $washed = rcmail_print_body($part); |
| | | |
| | | $this->assertRegExp('/<p>символ<\/p>/', $washed, "Remove non-unicode characters from HTML message body"); |
| | | } |
| | | |
| | | /** |
| | | * Test links pattern replacements in plaintext messages |
| | | */ |
| | | function test_plaintext() |
| | | { |
| | | $part = new rcube_message_part; |
| | | $part->ctype_primary = 'text'; |
| | | $part->ctype_secondary = 'plain'; |
| | | $part->body = quoted_printable_decode(file_get_contents(TESTS_DIR . 'src/plainbody.txt')); |
| | | $html = rcmail_print_body($part, array('safe' => true)); |
| | | |
| | | $this->assertRegExp('/<a href="mailto:nobody@roundcube.net" onclick="return rcmail.command\(\'compose\',\'nobody@roundcube.net\',this\)">nobody@roundcube.net<\/a>/', $html, "Mailto links with onclick"); |
| | | $this->assertRegExp('#<a href="http://www.apple.com/legal/privacy" target="_blank">http://www.apple.com/legal/privacy</a>#', $html, "Links with target=_blank"); |
| | | $this->assertRegExp('#\\[<a href="http://example.com/\\?tx\\[a\\]=5" target="_blank">http://example.com/\\?tx\\[a\\]=5</a>\\]#', $html, "Links with square brackets"); |
| | | } |
| | | |
| | | /** |
| | | * Test mailto links in html messages |
| | | */ |
| | | function test_mailto() |
| | | { |
| | | $part = $this->get_html_part('src/mailto.txt'); |
| | | |
| | | // render HTML in normal mode |
| | | $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo'); |
| | | |
| | | $mailto = '<a href="mailto:me@me.com?subject=this is the subject&body=this is the body"' |
| | | .' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&body=this is the body\',this)">e-mail</a>'; |
| | | |
| | | $this->assertRegExp('|'.preg_quote($mailto, '|').'|', $html, "Extended mailto links"); |
| | | } |
| | | |
| | | /** |
| | | * Test the elimination of HTML comments |
| | | */ |
| | | function test_html_comments() |
| | | { |
| | | $part = $this->get_html_part('src/htmlcom.txt'); |
| | | $washed = rcmail_print_body($part, array('safe' => true)); |
| | | |
| | | // #1487759 |
| | | $this->assertRegExp('|<p>test1</p>|', $washed, "Buggy HTML comments"); |
| | | // but conditional comments (<!--[if ...) should be removed |
| | | $this->assertNotRegExp('|<p>test2</p>|', $washed, "Conditional HTML comments"); |
| | | } |
| | | |
| | | /** |
| | | * Test URI base resolving in HTML messages |
| | | */ |
| | | function test_resolve_base() |
| | | { |
| | | $html = file_get_contents(TESTS_DIR . 'src/htmlbase.txt'); |
| | | $html = rcmail_resolve_base($html); |
| | | |
| | | $this->assertRegExp('|src="http://alec\.pl/dir/img1\.gif"|', $html, "URI base resolving [1]"); |
| | | $this->assertRegExp('|src="http://alec\.pl/dir/img2\.gif"|', $html, "URI base resolving [2]"); |
| | | $this->assertRegExp('|src="http://alec\.pl/img3\.gif"|', $html, "URI base resolving [3]"); |
| | | |
| | | // base resolving exceptions |
| | | $this->assertRegExp('|src="cid:theCID"|', $html, "URI base resolving exception [1]"); |
| | | $this->assertRegExp('|src="http://other\.domain\.tld/img3\.gif"|', $html, "URI base resolving exception [2]"); |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | tests/bootstrap.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2009-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Environment initialization script for unit tests | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | if (php_sapi_name() != 'cli') |
| | | die("Not in shell mode (php-cli)"); |
| | | |
| | | if (!defined('INSTALL_PATH')) define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' ); |
| | | |
| | | define('TESTS_DIR', dirname(__FILE__) . '/'); |
| | | |
| | | if (@is_dir(TESTS_DIR . 'config')) { |
| | | define('RCMAIL_CONFIG_DIR', TESTS_DIR . 'config'); |
| | | } |
| | | |
| | | require_once(INSTALL_PATH . 'program/include/iniset.php'); |
| | | |
| | | rcmail::get_instance()->config->set('devel_mode', false); |
New file |
| | |
| | | <phpunit backupGlobals="false" |
| | | bootstrap="bootstrap.php" |
| | | colors="true"> |
| | | <testsuites> |
| | | <testsuite name="All Tests"> |
| | | <file>Framework/BaseReplacer.php</file> |
| | | <file>Framework/Browser.php</file> |
| | | <file>Framework/Cache.php</file> |
| | | <file>Framework/Charset.php</file> |
| | | <file>Framework/ContentFilter.php</file> |
| | | <file>Framework/Html.php</file> |
| | | <file>Framework/Imap.php</file> |
| | | <file>Framework/ImapGeneric.php</file> |
| | | <file>Framework/Image.php</file> |
| | | <file>Framework/MessageHeader.php</file> |
| | | <file>Framework/MessagePart.php</file> |
| | | <file>Framework/Mime.php</file> |
| | | <file>Framework/Rcube.php</file> |
| | | <file>Framework/ResultIndex.php</file> |
| | | <file>Framework/ResultSet.php</file> |
| | | <file>Framework/ResultThread.php</file> |
| | | <file>Framework/Shared.php</file> |
| | | <file>Framework/Smtp.php</file> |
| | | <file>Framework/Spellchecker.php</file> |
| | | <file>Framework/StringReplacer.php</file> |
| | | <file>Framework/User.php</file> |
| | | <file>Framework/Utils.php</file> |
| | | <file>Framework/VCard.php</file> |
| | | <file>HtmlToText.php</file> |
| | | <file>MailFunc.php</file> |
| | | </testsuite> |
| | | <testsuite name="managesieve"> |
| | | <file>./../plugins/managesieve/tests/Parser.php</file> |
| | | <file>./../plugins/managesieve/tests/Tokenizer.php</file> |
| | | </testsuite> |
| | | </testsuites> |
| | | </phpunit> |
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
|