diff options
Diffstat (limited to 'program')
42 files changed, 683 insertions, 317 deletions
diff --git a/program/include/bc.php b/program/include/bc.php index df018320c..0ddfb3215 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -62,7 +62,7 @@ function rcmail_url($action, $p=array(), $task=null) function rcmail_temp_gc() { - $rcmail = rcmail::get_instance()->temp_gc(); + rcmail::get_instance()->temp_gc(); } function rcube_charset_convert($str, $from, $to=NULL) diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 7acb3490d..c16257d50 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -596,7 +596,7 @@ class rcmail extends rcube $post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); $post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST); - list($user, $domain) = explode('@', $post_user); + list(, $domain) = explode('@', $post_user); // direct match in default_host array if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) { @@ -700,28 +700,6 @@ class rcmail extends rcube /** - * Create unique authorization hash - * - * @param string Session ID - * @param int Timestamp - * @return string The generated auth hash - */ - private function get_auth_hash($sess_id, $ts) - { - $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s', - $sess_id, - $ts, - $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***', - $_SERVER['HTTP_USER_AGENT']); - - if (function_exists('sha1')) - return sha1($auth_string); - else - return md5($auth_string); - } - - - /** * Build a valid URL to this instance of Roundcube * * @param mixed Either a string with the action or url parameters as key-value pairs @@ -1167,7 +1145,7 @@ class rcmail extends rcube */ public function table_output($attrib, $table_data, $a_show_cols, $id_col) { - $table = new html_table(/*array('cols' => count($a_show_cols))*/); + $table = new html_table($attrib); // add table header if (!$attrib['noheader']) { @@ -1532,7 +1510,7 @@ class rcmail extends rcube $collapsed = $this->config->get('collapsed_folders'); $out = ''; - foreach ($arrFolders as $key => $folder) { + foreach ($arrFolders as $folder) { $title = null; $folder_class = $this->folder_classname($folder['id']); $is_collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false; @@ -1618,7 +1596,7 @@ class rcmail extends rcube { $out = ''; - foreach ($arrFolders as $key => $folder) { + foreach ($arrFolders as $folder) { // skip exceptions (and its subfolders) if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) { continue; diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index d8996edbf..02eef2fd1 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -731,14 +731,13 @@ class rcmail_output_html extends rcmail_output /** * Determines if a given condition is met * - * @todo Get rid off eval() once I understand what this does. * @todo Extend this to allow real conditions, not just "set" * @param string Condition statement * @return boolean True if condition is met, False if not */ protected function check_condition($condition) { - return eval("return (".$this->parse_expression($condition).");"); + return $this->eval_expression($condition); } @@ -760,14 +759,15 @@ class rcmail_output_html extends rcmail_output /** - * Parses expression and replaces variables + * Parse & evaluate a given expression and return its result. * - * @param string Expression statement - * @return string Expression value + * @param string Expression statement + * + * @return mixed Expression result */ - protected function parse_expression($expression) + protected function eval_expression ($expression) { - return preg_replace( + $expression = preg_replace( array( '/session:([a-z0-9_]+)/i', '/config:([a-z0-9_]+)(:([a-z0-9_]+))?/i', @@ -779,14 +779,29 @@ class rcmail_output_html extends rcmail_output ), array( "\$_SESSION['\\1']", - "\$this->app->config->get('\\1',rcube_utils::get_boolean('\\3'))", - "\$this->env['\\1']", + "\$app->config->get('\\1',rcube_utils::get_boolean('\\3'))", + "\$env['\\1']", "rcube_utils::get_input_value('\\1', rcube_utils::INPUT_GPC)", "\$_COOKIE['\\1']", - "\$this->browser->{'\\1'}", + "\$browser->{'\\1'}", $this->template_name, ), - $expression); + $expression + ); + + $fn = create_function('$app,$browser,$env', "return ($expression);"); + if (!$fn) { + rcube::raise_error(array( + 'code' => 505, + 'type' => 'php', + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => "Expression parse error on: ($expression)"), true, false); + + return null; + } + + return $fn($this->app, $this->browser, $this->env); } @@ -839,7 +854,7 @@ class rcmail_output_html extends rcmail_output // show a label case 'label': if ($attrib['expression']) - $attrib['name'] = eval("return " . $this->parse_expression($attrib['expression']) .";"); + $attrib['name'] = $this->eval_expression($attrib['expression']); if ($attrib['name'] || $attrib['command']) { // @FIXME: 'noshow' is useless, remove? @@ -971,8 +986,7 @@ class rcmail_output_html extends rcmail_output // return code for a specified eval expression case 'exp': - $value = $this->parse_expression($attrib['expression']); - return eval("return html::quote($value);"); + return html::quote($this->eval_expression($attrib['expression'])); // return variable case 'var': diff --git a/program/js/app.js b/program/js/app.js index 474a1b8c3..af09572ff 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -936,16 +936,13 @@ function rcube_webmail() url._to = props; } else { - // use contact_id passed as command parameter - var n, len, a_cids = []; + var a_cids = []; + // use contact id passed as command parameter if (props) a_cids.push(props); // get selected contacts - else if (this.contact_list) { - var selection = this.contact_list.get_selection(); - for (n=0, len=selection.length; n<len; n++) - a_cids.push(selection[n]); - } + else if (this.contact_list) + a_cids = this.contact_list.get_selection(); if (a_cids.length) this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source }, true); @@ -1582,7 +1579,7 @@ function rcube_webmail() this.msglist_set_coltypes = function(list) { - var i, found, name, cols = list.list.tHead.rows[0].cells; + var i, found, name, cols = list.thead.rows[0].cells; this.env.coltypes = []; @@ -1632,13 +1629,17 @@ function rcube_webmail() this.open_window = function(url, width, height) { - var w = Math.min(width, screen.width - 10), - h = Math.min(height, screen.height - 100), - l = (screen.width - w) / 2 + (screen.left || 0), - t = Math.max(0, (screen.height - h) / 2 + (screen.top || 0) - 20), + var dh = (window.outerHeight || 0) - (window.innerHeight || 0), + dw = (window.outerWidth || 0) - (window.innerWidth || 0), + sh = screen.availHeight || screen.height, + sw = screen.availWidth || screen.width, + w = Math.min(width, sw), + h = Math.min(height, sh), + l = Math.max(0, (sw - w) / 2 + (screen.left || 0)), + t = Math.max(0, (sh - h) / 2 + (screen.top || 0)), wname = 'rcmextwin' + new Date().getTime(), extwin = window.open(url + (url.match(/\?/) ? '&' : '?') + '_extwin=1', wname, - 'width='+w+',height='+h+',top='+t+',left='+l+',resizable=yes,toolbar=no,status=no,location=no'); + 'width='+(w-dw)+',height='+(h-dh)+',top='+t+',left='+l+',resizable=yes,toolbar=no,status=no,location=no'); // write loading... message to empty windows if (!url && extwin.document) { @@ -1732,10 +1733,7 @@ function rcube_webmail() + (flags.flagged ? ' flagged' : '') + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '') + (message.selected ? ' selected' : ''), - // for performance use DOM instead of jQuery here - row = document.createElement('tr'); - - row.id = 'rcmrow'+uid; + row = { cols:[], style:{}, id:'rcmrow'+uid }; // message status icons css_class = 'msgicon'; @@ -1799,8 +1797,7 @@ function rcube_webmail() // add each submitted col for (n in this.env.coltypes) { c = this.env.coltypes[n]; - col = document.createElement('td'); - col.className = String(c).toLowerCase(); + col = { className: String(c).toLowerCase() }; if (c == 'flag') { css_class = (flags.flagged ? 'flagged' : 'unflagged'); @@ -1845,8 +1842,7 @@ function rcube_webmail() html = cols[c]; col.innerHTML = html; - - row.appendChild(col); + row.cols.push(col); } list.insert_row(row, attop); @@ -2209,7 +2205,7 @@ function rcube_webmail() if (root) row = rows[root] ? rows[root].obj : null; else - row = this.message_list.list.tBodies[0].firstChild; + row = this.message_list.tbody.firstChild; while (row) { if (row.nodeType == 1 && (r = rows[row.uid])) { @@ -2385,7 +2381,7 @@ function rcube_webmail() this.delete_excessive_thread_rows = function() { var rows = this.message_list.rows, - tbody = this.message_list.list.tBodies[0], + tbody = this.message_list.tbody, row = tbody.firstChild, cnt = this.env.pagesize + 1; @@ -4328,21 +4324,7 @@ function rcube_webmail() newcid = newcid+'-'+source; } - if (list.rows[cid] && (row = list.rows[cid].obj)) { - for (c=0; c<cols_arr.length; c++) - if (row.cells[c]) - $(row.cells[c]).html(cols_arr[c]); - - // cid change - if (newcid) { - newcid = this.html_identifier(newcid); - row.id = 'rcmrow' + newcid; - list.remove_row(cid); - list.init_row(row); - list.selection[0] = newcid; - row.style.display = ''; - } - } + list.update_row(cid, cols_arr, newcid, true); }; // add row to contacts list @@ -4352,7 +4334,7 @@ function rcube_webmail() return false; var c, col, list = this.contact_list, - row = document.createElement('tr'); + row = { cols:[] }; row.id = 'rcmrow'+this.html_identifier(cid); row.className = 'contact ' + (classes || ''); @@ -4362,10 +4344,10 @@ function rcube_webmail() // add each submitted col for (c in cols) { - col = document.createElement('td'); + col = {}; col.className = String(c).toLowerCase(); col.innerHTML = cols[c]; - row.appendChild(col); + row.cols.push(col); } list.insert_row(row); @@ -4471,11 +4453,22 @@ function rcube_webmail() this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); }); this.name_input_li = $('<li>').addClass(type).append(this.name_input); - var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : $('ul.groups li:last', this.get_folder_li(this.env.source,'',true)); + var ul, li; + + // find list (UL) element + if (type == 'contactsearch') + ul = this.gui_objects.folderlist; + else + ul = $('ul.groups', this.get_folder_li(this.env.source,'',true)); + + // append to the list + li = $('li:last', ul); if (li.length) this.name_input_li.insertAfter(li); - else - this.name_input_li.appendTo(type == 'contactsearch' ? this.gui_objects.folderlist : $('ul.groups', this.get_folder_li(this.env.source,'',true))); + else { + this.name_input_li.appendTo(ul); + ul.show(); // make sure the list is visible + } } this.name_input.select().focus(); @@ -4532,11 +4525,13 @@ function rcube_webmail() this.reset_add_input = function() { if (this.name_input) { + var li = this.name_input.parent(); if (this.env.group_renaming) { - var li = this.name_input.parent(); li.children().last().show(); this.env.group_renaming = false; } + else if ($('li', li.parent()).length == 1) + li.parent().hide(); this.name_input.remove(); @@ -4964,18 +4959,16 @@ function rcube_webmail() this.update_identity_row = function(id, name, add) { - var row, col, list = this.identity_list, + var list = this.identity_list, rid = this.html_identifier(id); - if (list.rows[rid] && (row = list.rows[rid].obj)) { - $(row.cells[0]).html(name); - } - else if (add) { - row = $('<tr>').attr('id', 'rcmrow'+rid).get(0); - col = $('<td>').addClass('mail').html(name).appendTo(row); - list.insert_row(row); + if (add) { + list.insert_row({ id:'rcmrow'+rid, cols:[ { className:'mail', innerHTML:name } ] }); list.select(rid); } + else { + list.update_row(rid, [ name ]); + } }; @@ -5750,7 +5743,7 @@ function rcube_webmail() this.set_message_coltypes = function(coltypes, repl, smart_col) { var list = this.message_list, - thead = list ? list.list.tHead : null, + thead = list ? list.thead : null, cell, col, n, len, th, tr; this.env.coltypes = coltypes; diff --git a/program/js/list.js b/program/js/list.js index e689c6722..fbebc8a20 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -30,6 +30,9 @@ function rcube_list_widget(list, p) this.BACKSPACE_KEY = 8; this.list = list ? list : null; + this.tagname = this.list ? this.list.nodeName.toLowerCase() : 'table'; + this.thead; + this.tbody; this.frame = null; this.rows = []; this.selection = []; @@ -56,7 +59,7 @@ function rcube_list_widget(list, p) this.focused = false; this.drag_mouse_start = null; this.dblclick_time = 600; - this.row_init = function(){}; + this.row_init = function(){}; // @deprecated; use list.addEventListener('initrow') instead // overwrite default paramaters if (p && typeof p === 'object') @@ -73,11 +76,19 @@ rcube_list_widget.prototype = { */ init: function() { - if (this.list && this.list.tBodies[0]) { + if (this.tagname == 'table' && this.list && this.list.tBodies[0]) { + this.thead = this.list.tHead; + this.tbody = this.list.tBodies[0]; + } + else if (this.tagname != 'table' && this.list) { + this.tbody = this.list; + } + + if (this.tbody) { this.rows = []; this.rowcount = 0; - var r, len, rows = this.list.tBodies[0].rows; + var r, len, rows = this.tbody.childNodes; for (r=0, len=rows.length; r<len; r++) { this.init_row(rows[r]); @@ -127,7 +138,8 @@ init_row: function(row) if (document.all) row.onselectstart = function() { return false; }; - this.row_init(this.rows[uid]); + this.row_init(this.rows[uid]); // legacy support + this.triggerEvent('initrow', this.rows[uid]); } }, @@ -137,16 +149,16 @@ init_row: function(row) */ init_header: function() { - if (this.list && this.list.tHead) { + if (this.thead) { this.colcount = 0; var col, r, p = this; // add events for list columns moving - if (this.column_movable && this.list.tHead && this.list.tHead.rows) { - for (r=0; r<this.list.tHead.rows[0].cells.length; r++) { + if (this.column_movable && this.thead && this.thead.rows) { + for (r=0; r<this.thead.rows[0].cells.length; r++) { if (this.column_fixed == r) continue; - col = this.list.tHead.rows[0].cells[r]; + col = this.thead.rows[0].cells[r]; col.onmousedown = function(e){ return p.drag_column(e, this); }; this.colcount++; } @@ -160,10 +172,16 @@ init_header: function() */ clear: function(sel) { - var tbody = document.createElement('tbody'); + if (this.tagname == 'table') { + var tbody = document.createElement('tbody'); + this.list.insertBefore(tbody, this.tbody); + this.list.removeChild(this.list.tBodies[1]); + this.tbody = tbody; + } + else { + $(this.row_tagname() + ':not(.thead)', this.tbody).remove(); + } - this.list.insertBefore(tbody, this.list.tBodies[0]); - this.list.removeChild(this.list.tBodies[1]); this.rows = []; this.rowcount = 0; @@ -181,12 +199,12 @@ clear: function(sel) */ remove_row: function(uid, sel_next) { - var obj = this.rows[uid] ? this.rows[uid].obj : null; + var node = this.rows[uid] ? this.rows[uid].obj : null; - if (!obj) + if (!node) return; - obj.style.display = 'none'; + node.style.display = 'none'; if (sel_next) this.select_next(); @@ -201,9 +219,28 @@ remove_row: function(uid, sel_next) */ insert_row: function(row, before) { - var tbody = this.list.tBodies[0]; + var tbody = this.tbody; + + // create a real dom node first + if (row.nodeName === undefined) { + // for performance reasons use DOM instead of jQuery here + var domrow = document.createElement(this.row_tagname()); + if (row.id) domrow.id = row.id; + if (row.className) domrow.className = row.className; + if (row.style) $.extend(domrow.style, row.style); + + for (var domcell, col, i=0; row.cols && i < row.cols.length; i++) { + col = row.cols[i]; + domcell = document.createElement(this.col_tagname()); + if (col.className) domcell.className = col.className; + if (col.innerHTML) domcell.innerHTML = col.innerHTML; + domrow.appendChild(domcell); + } + + row = domrow; + } - if (before && tbody.rows.length) + if (before && tbody.childNodes.length) tbody.insertBefore(row, (typeof before == 'object' && before.parentNode == tbody) ? before : tbody.firstChild); else tbody.appendChild(row); @@ -212,6 +249,28 @@ insert_row: function(row, before) this.rowcount++; }, +/** + * + */ +update_row: function(id, cols, newid, select) +{ + var row = this.rows[id]; + if (!row) return false; + + var domrow = row.obj; + for (var domcell, col, i=0; cols && i < cols.length; i++) { + this.get_cell(domrow, i).html(cols[i]); + } + + if (newid) { + delete this.rows[id]; + domrow.id = 'rcmrow' + newid; + this.init_row(domrow); + + if (select) + this.selection[0] = newid; + } +}, /** @@ -230,9 +289,9 @@ focus: function(e) } // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620) + // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058) $(':focus:not(body)').blur(); - // un-focus iframe bodies (#1489058), this doesn't work in Opera and Chrome - $('iframe').contents().find('body').blur(); + window.focus(); if (e || (e = window.event)) rcube_event.cancel(e); @@ -271,8 +330,8 @@ drag_column: function(e, col) this.add_dragfix(); // find selected column number - for (var i=0; i<this.list.tHead.rows[0].cells.length; i++) { - if (col == this.list.tHead.rows[0].cells[i]) { + for (var i=0; i<this.thead.rows[0].cells.length; i++) { + if (col == this.thead.rows[0].cells[i]) { this.selected_column = i; break; } @@ -451,7 +510,7 @@ expand: function(row) this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj }); } else { - var tbody = this.list.tBodies[0]; + var tbody = this.tbody; new_row = tbody.firstChild; depth = 0; last_expanded_parent_depth = 0; @@ -504,7 +563,7 @@ collapse_all: function(row) return false; } else { - new_row = this.list.tBodies[0].firstChild; + new_row = this.tbody.firstChild; depth = 0; } @@ -543,7 +602,7 @@ expand_all: function(row) this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj }); } else { - new_row = this.list.tBodies[0].firstChild; + new_row = this.tbody.firstChild; depth = 0; } @@ -611,7 +670,7 @@ get_prev_row: function() get_first_row: function() { if (this.rowcount) { - var i, len, rows = this.list.tBodies[0].rows; + var i, len, rows = this.tbody.childNodes; for (i=0, len=rows.length-1; i<len; i++) if (rows[i].id && String(rows[i].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null) @@ -624,7 +683,7 @@ get_first_row: function() get_last_row: function() { if (this.rowcount) { - var i, rows = this.list.tBodies[0].rows; + var i, rows = this.tbody.childNodes; for (i=rows.length-1; i>=0; i--) if (rows[i].id && String(rows[i].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null) @@ -634,6 +693,22 @@ get_last_row: function() return null; }, +row_tagname: function() +{ + var row_tagnames = { table:'tr', ul:'li', '*':'div' }; + return row_tagnames[this.tagname] || row_tagnames['*']; +}, + +col_tagname: function() +{ + var col_tagnames = { table:'td', '*':'span' }; + return col_tagnames[this.tagname] || col_tagnames['*']; +}, + +get_cell: function(row, index) +{ + return $(this.col_tagname(), row).eq(index); +}, /** * selects or unselects the proper row depending on the modifier key pressed @@ -781,19 +856,19 @@ shift_select: function(id, control) this.shift_start = id; var n, i, j, to_row = this.rows[id], - from_rowIndex = this.rows[this.shift_start].obj.rowIndex, - to_rowIndex = to_row.obj.rowIndex; + from_rowIndex = this._rowIndex(this.rows[this.shift_start].obj), + to_rowIndex = this._rowIndex(to_row.obj); if (!to_row.expanded && to_row.has_children) if (to_row = this.rows[(this.row_children(id)).pop()]) - to_rowIndex = to_row.obj.rowIndex; + to_rowIndex = this._rowIndex(to_row.obj); i = ((from_rowIndex < to_rowIndex) ? from_rowIndex : to_rowIndex), j = ((from_rowIndex > to_rowIndex) ? from_rowIndex : to_rowIndex); // iterate through the entire message list for (n in this.rows) { - if (this.rows[n].obj.rowIndex >= i && this.rows[n].obj.rowIndex <= j) { + if (this._rowIndex(this.rows[n].obj) >= i && this._rowIndex(this.rows[n].obj) <= j) { if (!this.in_selection(n)) { this.highlight_row(n, true); } @@ -806,6 +881,13 @@ shift_select: function(id, control) } }, +/** + * Helper method to emulate the rowIndex property of non-tr elements + */ +_rowIndex: function(obj) +{ + return (obj.rowIndex !== undefined) ? obj.rowIndex : $(obj).prevAll().length; +}, /** * Check if given id is part of the current selection @@ -1150,7 +1232,7 @@ drag_mouse_move: function(e) this.draglayer.html(''); // get subjects of selected messages - var i, n, obj; + var i, n, obj, me; for (n=0; n<this.selection.length; n++) { // only show 12 lines if (n>12) { @@ -1158,29 +1240,28 @@ drag_mouse_move: function(e) break; } + me = this; if (obj = this.rows[this.selection[n]].obj) { - for (i=0; i<obj.childNodes.length; i++) { - if (obj.childNodes[i].nodeName == 'TD') { - if (n == 0) - this.drag_start_pos = $(obj.childNodes[i]).offset(); + $('> '+this.col_tagname(), obj).each(function(i,elem){ + if (n == 0) + me.drag_start_pos = $(elem).offset(); - if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == i)) { - var subject = $(obj.childNodes[i]).text(); - - if (!subject) - break; + if (me.subject_col < 0 || (me.subject_col >= 0 && me.subject_col == i)) { + var subject = $(elem).text(); + if (subject) { // remove leading spaces subject = $.trim(subject); // truncate line to 50 characters subject = (subject.length > 50 ? subject.substring(0, 50) + '...' : subject); var entry = $('<div>').text(subject); - this.draglayer.append(entry); - break; + me.draglayer.append(entry); } + + return false; // break } - } + }); } } @@ -1255,7 +1336,7 @@ column_drag_mouse_move: function(e) if (!this.col_draglayer) { var lpos = $(this.list).offset(), - cells = this.list.tHead.rows[0].cells; + cells = this.thead.rows[0].cells; // create dragging layer this.col_draglayer = $('<div>').attr('id', 'rcmcoldraglayer') @@ -1411,7 +1492,11 @@ del_dragfix: function() */ column_replace: function(from, to) { - var len, cells = this.list.tHead.rows[0].cells, + // only supported for <table> lists + if (!this.thead || !this.thead.rows) + return; + + var len, cells = this.thead.rows[0].cells, elem = cells[from], before = cells[to], td = document.createElement('td'); @@ -1424,8 +1509,8 @@ column_replace: function(from, to) cells[0].parentNode.replaceChild(elem, td); // replace list cells - for (r=0, len=this.list.tBodies[0].rows.length; r<len; r++) { - row = this.list.tBodies[0].rows[r]; + for (r=0, len=this.tbody.rows.length; r<len; r++) { + row = this.tbody.rows[r]; elem = row.cells[from]; before = row.cells[to]; diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php index dbc9ca51f..830ada9c2 100644 --- a/program/lib/Roundcube/html.php +++ b/program/lib/Roundcube/html.php @@ -678,6 +678,11 @@ class html_table extends html { $default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array(); $this->attrib = array_merge($attrib, $default_attrib); + + if (!empty($attrib['tagname']) && $attrib['tagname'] != 'table') { + $this->tagname = $attrib['tagname']; + $this->allowed = self::$common_attrib; + } } /** @@ -816,19 +821,20 @@ class html_table extends html if (!empty($this->header)) { $rowcontent = ''; foreach ($this->header as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } - $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)); + $thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) : + self::tag($this->_row_tagname(), array('class' => 'thead'), $rowcontent, parent::$common_attrib); } foreach ($this->rows as $r => $row) { $rowcontent = ''; foreach ($row->cells as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } if ($r < $this->rowindex || count($row->cells)) { - $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib); + $tbody .= self::tag($this->_row_tagname(), $row->attrib, $rowcontent, parent::$common_attrib); } } @@ -837,7 +843,7 @@ class html_table extends html } // add <tbody> - $this->content = $thead . self::tag('tbody', null, $tbody); + $this->content = $thead . ($this->tagname == 'table' ? self::tag('tbody', null, $tbody) : $tbody); unset($this->attrib['cols'], $this->attrib['rowsonly']); return parent::show(); @@ -862,4 +868,22 @@ class html_table extends html $this->rowindex = 0; } + /** + * Getter for the corresponding tag name for table row elements + */ + private function _row_tagname() + { + static $row_tagnames = array('table' => 'tr', 'ul' => 'li', '*' => 'div'); + return $row_tagnames[$this->tagname] ?: $row_tagnames['*']; + } + + /** + * Getter for the corresponding tag name for table cell elements + */ + private function _col_tagname() + { + static $col_tagnames = array('table' => 'td', '*' => 'span'); + return $col_tagnames[$this->tagname] ?: $col_tagnames['*']; + } + } diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php index cbc3c6773..d23ad3687 100644 --- a/program/lib/Roundcube/rcube_addressbook.php +++ b/program/lib/Roundcube/rcube_addressbook.php @@ -309,9 +309,14 @@ abstract class rcube_addressbook * List all active contact groups of this source * * @param string Optional search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { /* empty for address books don't supporting groups */ return array(); @@ -370,9 +375,10 @@ abstract class rcube_addressbook /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -383,9 +389,10 @@ abstract class rcube_addressbook /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) { @@ -425,7 +432,7 @@ abstract class rcube_addressbook $out = array_merge($out, (array)$values); } else { - list($f, $type) = explode(':', $c); + list(, $type) = explode(':', $c); $out[$type] = array_merge((array)$out[$type], (array)$values); } } @@ -528,7 +535,7 @@ abstract class rcube_addressbook */ public static function compose_contact_key($contact, $sort_col) { - $key = $contact[$sort_col] . ':' . $row['sourceid']; + $key = $contact[$sort_col] . ':' . $contact['sourceid']; // add email to a key to not skip contacts with the same name (#1488375) if (!empty($contact['email'])) { @@ -538,7 +545,6 @@ abstract class rcube_addressbook return $key; } - /** * Compare search value with contact data * diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index e4fd7dc10..3919cdc6e 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -137,16 +137,34 @@ class rcube_contacts extends rcube_addressbook * List all active contact groups of this source * * @param string Search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { $results = array(); if (!$this->groups) return $results; - $sql_filter = $search ? " AND " . $this->db->ilike('name', '%'.$search.'%') : ''; + if ($search) { + switch (intval($mode)) { + case 1: + $sql_filter = $this->db->ilike('name', $search); + break; + case 2: + $sql_filter = $this->db->ilike('name', $search . '%'); + break; + default: + $sql_filter = $this->db->ilike('name', '%' . $search . '%'); + } + + $sql_filter = " AND $sql_filter"; + } $sql_result = $this->db->query( "SELECT * FROM ".$this->db->table_name($this->db_groups). @@ -879,9 +897,10 @@ class rcube_contacts extends rcube_addressbook /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -926,9 +945,10 @@ class rcube_contacts extends rcube_addressbook /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) { diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php index 0d3276b84..fb8d8f103 100644 --- a/program/lib/Roundcube/rcube_csv2vcard.php +++ b/program/lib/Roundcube/rcube_csv2vcard.php @@ -130,6 +130,21 @@ class rcube_csv2vcard 'work_state' => 'region:work', 'home_city_short' => 'locality:home', 'home_state_short' => 'region:home', + + // Atmail + 'date_of_birth' => 'birthday', + 'email' => 'email:pref', + 'home_mobile' => 'phone:cell', + 'home_zip' => 'zipcode:home', + 'info' => 'notes', + 'user_photo' => 'photo', + 'url' => 'website:homepage', + 'work_company' => 'organization', + 'work_dept' => 'departament', + 'work_fax' => 'phone:work,fax', + 'work_mobile' => 'phone:work,cell', + 'work_title' => 'jobtitle', + 'work_zip' => 'zipcode:work', ); /** @@ -230,8 +245,29 @@ class rcube_csv2vcard 'work_phone' => "Work Phone", 'work_address' => "Work Address", //'work_address_2' => "Work Address 2", + 'work_city' => "Work City", 'work_country' => "Work Country", + 'work_state' => "Work State", 'work_zipcode' => "Work ZipCode", + + // Atmail + 'date_of_birth' => "Date of Birth", + 'email' => "Email", + //'email_2' => "Email2", + //'email_3' => "Email3", + //'email_4' => "Email4", + //'email_5' => "Email5", + 'home_mobile' => "Home Mobile", + 'home_zip' => "Home Zip", + 'info' => "Info", + 'user_photo' => "User Photo", + 'url' => "URL", + 'work_company' => "Work Company", + 'work_dept' => "Work Dept", + 'work_fax' => "Work Fax", + 'work_mobile' => "Work Mobile", + 'work_title' => "Work Title", + 'work_zip' => "Work Zip", ); protected $local_label_map = array(); @@ -268,7 +304,6 @@ class rcube_csv2vcard { // convert to UTF-8 $head = substr($csv, 0, 4096); - $fallback = rcube::get_instance()->config->get('default_charset', 'ISO-8859-1'); // fallback to Latin-1? $charset = rcube_charset::detect($head, RCUBE_CHARSET); $csv = rcube_charset::convert($csv, $charset); $head = ''; @@ -276,7 +311,7 @@ class rcube_csv2vcard $this->map = array(); // Parse file - foreach (preg_split("/[\r\n]+/", $csv) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $csv) as $line) { $elements = $this->parse_line($line); if (empty($elements)) { continue; @@ -353,6 +388,12 @@ class rcube_csv2vcard if (!empty($this->local_label_map)) { for ($i = 0; $i < $size; $i++) { $label = $this->local_label_map[$elements[$i]]; + + // special localization label + if ($label && $label[0] == '_') { + $label = substr($label, 1); + } + if ($label && !empty($this->csv2vcard_map[$label])) { $map2[$i] = $this->csv2vcard_map[$label]; } @@ -384,9 +425,13 @@ class rcube_csv2vcard $contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; } + // Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" foreach (array('birthday', 'anniversary') as $key) { - if (!empty($contact[$key]) && $contact[$key] == '0/0/00') { // @TODO: localization? - unset($contact[$key]); + if (!empty($contact[$key])) { + $date = preg_replace('/[0[:^word:]]/', '', $contact[$key]); + if (empty($date)) { + unset($contact[$key]); + } } } diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index d86e3dd98..4b9ab131c 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -128,7 +128,7 @@ class rcube_db $dsn_string = $this->dsn_string($dsn); $dsn_options = $this->dsn_options($dsn); - if ($db_pconn) { + if ($this->db_pconn) { $dsn_options[PDO::ATTR_PERSISTENT] = true; } @@ -405,21 +405,22 @@ class rcube_db $this->db_error_msg = null; // send query - $query = $this->dbh->query($query); + $result = $this->dbh->query($query); - if ($query === false) { + if ($result === false) { $error = $this->dbh->errorInfo(); $this->db_error = true; $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); rcube::raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg), true, false); + 'message' => $this->db_error_msg . " (SQL Query: $query)" + ), true, false); } - $this->last_result = $query; + $this->last_result = $result; - return $query; + return $result; } /** diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index 8ab6403c8..b2cbab271 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -127,7 +127,7 @@ class rcube_db_mysql extends rcube_db $result[PDO::MYSQL_ATTR_FOUND_ROWS] = true; // Enable AUTOCOMMIT mode (#1488902) - $dsn_options[PDO::ATTR_AUTOCOMMIT] = true; + $result[PDO::ATTR_AUTOCOMMIT] = true; return $result; } @@ -147,7 +147,7 @@ class rcube_db_mysql extends rcube_db $result = $this->query('SHOW VARIABLES'); - while ($sql_arr = $this->fetch_array($result)) { + while ($row = $this->fetch_array($result)) { $this->variables[$row[0]] = $row[1]; } } diff --git a/program/lib/Roundcube/rcube_enriched.php b/program/lib/Roundcube/rcube_enriched.php index 8c628c912..12deb33ce 100644 --- a/program/lib/Roundcube/rcube_enriched.php +++ b/program/lib/Roundcube/rcube_enriched.php @@ -118,7 +118,7 @@ class rcube_enriched $quoted = ''; $lines = explode('<br>', $a[2]); - foreach ($lines as $n => $line) + foreach ($lines as $line) $quoted .= '>'.$line.'<br>'; $body = $a[1].'<span class="quotes">'.$quoted.'</span>'.$a[3]; diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index c67985186..43c61fd81 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -981,7 +981,7 @@ class rcube_imap extends rcube_storage // use memory less expensive (and quick) method for big result set $index = clone $this->index('', $this->sort_field, $this->sort_order); // get messages uids for one page... - $index->slice($start_msg, min($cnt-$from, $this->page_size)); + $index->slice($from, min($cnt-$from, $this->page_size)); if ($slice) { $index->slice(-$slice, $slice); @@ -1423,8 +1423,6 @@ class rcube_imap extends rcube_storage */ protected function search_index($folder, $criteria='ALL', $charset=NULL, $sort_field=NULL) { - $orig_criteria = $criteria; - if (!$this->check_connection()) { if ($this->threading) { return new rcube_result_thread(); @@ -2727,7 +2725,7 @@ class rcube_imap extends rcube_storage // filter folders list according to rights requirements if ($rights && $this->get_capability('ACL')) { - $a_folders = $this->filter_rights($a_folders, $rights); + $a_mboxes = $this->filter_rights($a_mboxes, $rights); } // filter folders and sort them @@ -2783,7 +2781,6 @@ class rcube_imap extends rcube_storage */ private function list_folders_update(&$result, $type = null) { - $delim = $this->get_hierarchy_delimiter(); $namespace = $this->get_namespace(); $search = array(); @@ -3846,7 +3843,7 @@ class rcube_imap extends rcube_storage $delimiter = $this->get_hierarchy_delimiter(); // find default folders and skip folders starting with '.' - foreach ($a_folders as $i => $folder) { + foreach ($a_folders as $folder) { if ($folder[0] == '.') { continue; } diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php index 748474af2..47d9aaf4a 100644 --- a/program/lib/Roundcube/rcube_imap_cache.php +++ b/program/lib/Roundcube/rcube_imap_cache.php @@ -430,7 +430,7 @@ class rcube_imap_cache ." AND uid = ?", $flags, $msg, $this->userid, $mailbox, (int) $message->uid); - if ($this->db->affected_rows()) { + if ($this->db->affected_rows($res)) { return; } } @@ -983,7 +983,7 @@ class rcube_imap_cache $uids, true, array('FLAGS'), $index['modseq'], $qresync); if (!empty($result)) { - foreach ($result as $id => $msg) { + foreach ($result as $msg) { $uid = $msg->uid; // Remove deleted message if ($this->skip_deleted && !empty($msg->flags['DELETED'])) { diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index db50ffbab..6c1b85552 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -746,7 +746,7 @@ class rcube_imap_generic } if ($this->prefs['timeout'] <= 0) { - $this->prefs['timeout'] = ini_get('default_socket_timeout'); + $this->prefs['timeout'] = max(0, intval(ini_get('default_socket_timeout'))); } // Connect @@ -1077,7 +1077,7 @@ class rcube_imap_generic } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'EXPUNGE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -1652,7 +1652,6 @@ class rcube_imap_generic } if (!empty($criteria)) { - $modseq = stripos($criteria, 'MODSEQ') !== false; $params .= ($params ? ' ' : '') . $criteria; } else { @@ -1791,7 +1790,6 @@ class rcube_imap_generic if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { $flags = explode(' ', strtoupper($matches[1])); if (in_array('\\DELETED', $flags)) { - $deleted[$id] = $id; continue; } } @@ -1936,7 +1934,7 @@ class rcube_imap_generic } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -1997,7 +1995,7 @@ class rcube_imap_generic } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -2172,7 +2170,7 @@ class rcube_imap_generic // create array with header field:data if (!empty($headers)) { $headers = explode("\n", trim($headers)); - foreach ($headers as $hid => $resln) { + foreach ($headers as $resln) { if (ord($resln[0]) <= 32) { $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln); } else { @@ -2180,7 +2178,7 @@ class rcube_imap_generic } } - while (list($lines_key, $str) = each($lines)) { + foreach ($lines as $str) { list($field, $string) = explode(':', $str, 2); $field = strtolower($field); @@ -2508,7 +2506,7 @@ class rcube_imap_generic $tokens = $this->tokenizeResponse(preg_replace('/(^\(|\)$)/', '', $line)); for ($i=0; $i<count($tokens); $i+=2) { - if (preg_match('/^(BODY|BINARY)/i', $token)) { + if (preg_match('/^(BODY|BINARY)/i', $tokens[$i])) { $result = $tokens[$i+1]; $found = true; break; @@ -3540,7 +3538,7 @@ class rcube_imap_generic if (is_array($element)) { reset($element); - while (list($key, $value) = each($element)) { + foreach ($element as $value) { $string .= ' ' . self::r_implode($value); } } diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index a2dd163e9..70163b21c 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -169,7 +169,7 @@ class rcube_ldap extends rcube_addressbook // Build sub_fields filter if (!empty($this->prop['sub_fields']) && is_array($this->prop['sub_fields'])) { $this->sub_filter = ''; - foreach ($this->prop['sub_fields'] as $attr => $class) { + foreach ($this->prop['sub_fields'] as $class) { if (!empty($class)) { $class = is_array($class) ? array_pop($class) : $class; $this->sub_filter .= '(objectClass=' . $class . ')'; @@ -1035,7 +1035,6 @@ class rcube_ldap extends rcube_addressbook $mail_field = $this->fieldmap['email']; // try to extract surname and firstname from displayname - $reverse_map = array_flip($this->fieldmap); $name_parts = preg_split('/[\s,.]+/', $save_data['name']); if ($sn_field && $missing[$sn_field]) { @@ -1107,7 +1106,7 @@ class rcube_ldap extends rcube_addressbook // Remove attributes that need to be added separately (child objects) $xfields = array(); if (!empty($this->prop['sub_fields']) && is_array($this->prop['sub_fields'])) { - foreach ($this->prop['sub_fields'] as $xf => $xclass) { + foreach (array_keys($this->prop['sub_fields']) as $xf) { if (!empty($newentry[$xf])) { $xfields[$xf] = $newentry[$xf]; unset($newentry[$xf]); @@ -1170,7 +1169,7 @@ class rcube_ldap extends rcube_addressbook } } - foreach ($this->fieldmap as $col => $fld) { + foreach ($this->fieldmap as $fld) { if ($fld) { $val = $ldap_data[$fld]; $old = $old_data[$fld]; @@ -1396,6 +1395,10 @@ class rcube_ldap extends rcube_addressbook */ protected function add_autovalues(&$attrs) { + if (empty($this->prop['autovalues'])) { + return; + } + $attrvals = array(); foreach ($attrs as $k => $v) { $attrvals['{'.$k.'}'] = is_array($v) ? $v[0] : $v; @@ -1403,13 +1406,24 @@ class rcube_ldap extends rcube_addressbook foreach ((array)$this->prop['autovalues'] as $lf => $templ) { if (empty($attrs[$lf])) { - // replace {attr} placeholders with concrete attribute values - $templ = preg_replace('/\{\w+\}/', '', strtr($templ, $attrvals)); + if (strpos($templ, '(') !== false) { + // replace {attr} placeholders with (escaped!) attribute values to be safely eval'd + $code = preg_replace('/\{\w+\}/', '', strtr($templ, array_map('addslashes', $attrvals))); + $fn = create_function('', "return ($code);"); + if (!$fn) { + rcube::raise_error(array( + 'code' => 505, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Expression parse error on: ($code)"), true, false); + continue; + } - if (strpos($templ, '(') !== false) - $attrs[$lf] = eval("return ($templ);"); - else - $attrs[$lf] = $templ; + $attrs[$lf] = $fn(); + } + else { + // replace {attr} placeholders with concrete attribute values + $attrs[$lf] = preg_replace('/\{\w+\}/', '', strtr($templ, $attrvals)); + } } } } @@ -1715,9 +1729,14 @@ class rcube_ldap extends rcube_addressbook * List all active contact groups of this source * * @param string Optional search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { if (!$this->groups) return array(); @@ -1729,10 +1748,10 @@ class rcube_ldap extends rcube_addressbook $groups = array(); if ($search) { - $search = mb_strtolower($search); foreach ($group_cache as $group) { - if (strpos(mb_strtolower($group['name']), $search) !== false) + if ($this->compare_search_value('name', $group['name'], $search, $mode)) { $groups[] = $group; + } } } else @@ -1763,7 +1782,7 @@ class rcube_ldap extends rcube_addressbook $vlv_active = $this->_vlv_set_controls($this->prop['groups'], $vlv_page+1, $page_size); } - $function = $this->_scope2func($this->prop['groups']['scope'], $ns_function); + $function = $this->_scope2func($this->prop['groups']['scope']); $res = @$function($this->conn, $base_dn, $filter, array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr))); if ($res === false) { @@ -1921,9 +1940,10 @@ class rcube_ldap extends rcube_addressbook /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $contact_ids) { @@ -1937,8 +1957,8 @@ class rcube_ldap extends rcube_addressbook $group_name = $group_cache[$group_id]['name']; $member_attr = $group_cache[$group_id]['member_attr']; $group_dn = "cn=$group_name,$base_dn"; + $new_attrs = array(); - $new_attrs = array(); foreach ($contact_ids as $id) $new_attrs[$member_attr][] = self::dn_decode($id); @@ -1949,28 +1969,32 @@ class rcube_ldap extends rcube_addressbook $this->cache->remove('groups'); - return count($new_attrs['member']); + return count($new_attrs[$member_attr]); } /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $contact_ids) { if (($group_cache = $this->cache->get('groups')) === null) $group_cache = $this->_fetch_groups(); + if (!is_array($contact_ids)) + $contact_ids = explode(',', $contact_ids); + $base_dn = $this->groups_base_dn; $group_name = $group_cache[$group_id]['name']; $member_attr = $group_cache[$group_id]['member_attr']; $group_dn = "cn=$group_name,$base_dn"; + $del_attrs = array(); - $del_attrs = array(); - foreach (explode(",", $contact_ids) as $id) + foreach ($contact_ids as $id) $del_attrs[$member_attr][] = self::dn_decode($id); if (!$this->ldap_mod_del($group_dn, $del_attrs)) { @@ -1980,7 +2004,7 @@ class rcube_ldap extends rcube_addressbook $this->cache->remove('groups'); - return count($del_attrs['member']); + return count($del_attrs[$member_attr]); } /** diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 9db1fa30a..a42d3fb28 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -362,7 +362,7 @@ class rcube_message // parse headers from message/rfc822 part if (!isset($structure->headers['subject']) && !isset($structure->headers['from'])) { - list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768)); + list($headers, ) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768)); $structure->headers = rcube_mime::parse_headers($headers); } } diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index 96296a57c..63549fbec 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -127,10 +127,11 @@ class rcube_mime * @param int $max List only this number of addresses * @param boolean $decode Decode address strings * @param string $fallback Fallback charset if none specified + * @param boolean $addronly Return flat array with e-mail addresses only * - * @return array Indexed list of addresses + * @return array Indexed list of addresses */ - static function decode_address_list($input, $max = null, $decode = true, $fallback = null) + static function decode_address_list($input, $max = null, $decode = true, $fallback = null, $addronly = false) { $a = self::parse_address_list($input, $decode, $fallback); $out = array(); @@ -145,20 +146,21 @@ class rcube_mime foreach ($a as $val) { $j++; $address = trim($val['address']); - $name = trim($val['name']); - if ($name && $address && $name != $address) - $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); - else if ($address) - $string = $address; - else if ($name) - $string = $name; - - $out[$j] = array( - 'name' => $name, - 'mailto' => $address, - 'string' => $string - ); + if ($addronly) { + $out[$j] = $address; + } + else { + $name = trim($val['name']); + if ($name && $address && $name != $address) + $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); + else if ($address) + $string = $address; + else if ($name) + $string = $name; + + $out[$j] = array('name' => $name, 'mailto' => $address, 'string' => $string); + } if ($max && $j==$max) break; @@ -476,9 +478,10 @@ class rcube_mime $q_level = 0; foreach ($text as $idx => $line) { - if ($line[0] == '>') { - // remove quote chars, store level in $q - $line = preg_replace('/^>+/', '', $line, -1, $q); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $q = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); // remove (optional) space-staffing $line = preg_replace('/^ /', '', $line); @@ -541,9 +544,10 @@ class rcube_mime foreach ($text as $idx => $line) { if ($line != '-- ') { - if ($line[0] == '>') { - // remove quote chars, store level in $level - $line = preg_replace('/^>+/', '', $line, -1, $level); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $level = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); // remove (optional) space-staffing and spaces before the line end $line = preg_replace('/(^ | +$)/', '', $line); $prefix = str_repeat('>', $level) . ' '; @@ -657,8 +661,8 @@ class rcube_mime $cutLength = $spacePos + 1; } else { - $subString = $string; - $cutLength = null; + $subString = $substr_func($string, 0, $breakPos, $charset); + $cutLength = $breakPos + 1; } } else { diff --git a/program/lib/Roundcube/rcube_output.php b/program/lib/Roundcube/rcube_output.php index b8ae86cf6..7ccf9a02e 100644 --- a/program/lib/Roundcube/rcube_output.php +++ b/program/lib/Roundcube/rcube_output.php @@ -162,7 +162,7 @@ abstract class rcube_output header("Cache-Control: private, must-revalidate"); } else { - header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0"); + header("Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); } } diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 4bb6c6677..33f04eaa5 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -313,7 +313,6 @@ class rcube_plugin_api $doc->loadXML($file); $xpath = new DOMXPath($doc); $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0"); - $data = array(); // XPaths of plugin metadata elements $metadata = array( diff --git a/program/lib/Roundcube/rcube_smtp.php b/program/lib/Roundcube/rcube_smtp.php index 5c7d2203c..0f3ac0407 100644 --- a/program/lib/Roundcube/rcube_smtp.php +++ b/program/lib/Roundcube/rcube_smtp.php @@ -119,7 +119,7 @@ class rcube_smtp } // try to connect to server and exit on failure - $result = $this->conn->connect($smtp_timeout); + $result = $this->conn->connect($CONFIG['smtp_timeout']); if (PEAR::isError($result)) { $this->response[] = "Connection failed: ".$result->getMessage(); @@ -433,9 +433,9 @@ class rcube_smtp $recipients = rcube_utils::explode_quoted_string(',', $recipients); reset($recipients); - while (list($k, $recipient) = each($recipients)) { + foreach ($recipients as $recipient) { $a = rcube_utils::explode_quoted_string(' ', $recipient); - while (list($k2, $word) = each($a)) { + foreach ($a as $word) { if (strpos($word, "@") > 0 && $word[strlen($word)-1] != '"') { $word = preg_replace('/^<|>$/', '', trim($word)); if (in_array($word, $addresses) === false) { diff --git a/program/lib/Roundcube/rcube_spellchecker.php b/program/lib/Roundcube/rcube_spellchecker.php index 816bcad2f..60aec500f 100644 --- a/program/lib/Roundcube/rcube_spellchecker.php +++ b/program/lib/Roundcube/rcube_spellchecker.php @@ -314,11 +314,6 @@ class rcube_spellchecker if (!$this->plink) { if (!extension_loaded('pspell')) { $this->error = "Pspell extension not available"; - rcube::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => $this->error), true, false); - return; } @@ -372,9 +367,19 @@ class rcube_spellchecker fclose($fp); } + // parse HTTP response + if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $store, $m)) { + $http_status = $m[1]; + if ($http_status != '200') + $this->error = 'HTTP ' . $m[1] . $m[2]; + } + if (!$store) { $this->error = "Empty result from spelling engine"; } + else if (preg_match('/<spellresult error="([^"]+)"/', $store, $m)) { + $this->error = "Error code $m[1] returned"; + } preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); @@ -588,7 +593,7 @@ class rcube_spellchecker if (empty($plugin['abort'])) { $dict = array(); - $this->rc->db->query( + $sql_result = $this->rc->db->query( "SELECT data FROM ".$this->rc->db->table_name('dictionary') ." WHERE user_id ". ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL") ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index fabe0f060..8467107fe 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -404,7 +404,7 @@ class rcube_utils $out = array(); $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); - foreach ($src as $key => $value) { + foreach (array_keys($src) as $key) { $fname = $key[0] == '_' ? substr($key, 1) : $key; if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) { $out[$fname] = self::get_input_value($key, $mode); diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php index 54bb9521d..90acb2110 100644 --- a/program/lib/Roundcube/rcube_vcard.php +++ b/program/lib/Roundcube/rcube_vcard.php @@ -90,7 +90,7 @@ class rcube_vcard */ public function __construct($vcard = null, $charset = RCUBE_CHARSET, $detect = false, $fieldmap = array()) { - if (!empty($fielmap)) { + if (!empty($fieldmap)) { $this->extend_fieldmap($fieldmap); } @@ -481,7 +481,7 @@ class rcube_vcard $vcard_block = ''; $in_vcard_block = false; - foreach (preg_split("/[\r\n]+/", $data) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $data) as $line) { if ($in_vcard_block && !empty($line)) { $vcard_block .= $line . "\n"; } @@ -784,9 +784,30 @@ class rcube_vcard } return $result; } + + $s = strtr($s, $rep2); + } + + // some implementations (GMail) use non-standard backslash before colon (#1489085) + // we will handle properly any backslashed character - removing dummy backslahes + // return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + + $s = str_replace("\r", '', $s); + $pos = 0; + + while (($pos = strpos($s, '\\', $pos)) !== false) { + $next = substr($s, $pos + 1, 1); + if ($next == 'n' || $next == 'N') { + $s = substr_replace($s, "\n", $pos, 2); + } + else { + $s = substr_replace($s, '', $pos, 1); + } + + $pos += 1; } - return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + return $s; } /** diff --git a/program/lib/utf8.class.php b/program/lib/utf8.class.php index e0dc9e2bd..0446159c7 100644 --- a/program/lib/utf8.class.php +++ b/program/lib/utf8.class.php @@ -60,8 +60,8 @@ Class utf8 { function loadCharset($charset) { $charset = preg_replace(array('/^WINDOWS-*125([0-8])$/', '/^CP-/'), array('CP125\\1', 'CP'), $charset); - if (isset($aliases[$charset])) - $charset = $aliases[$charset]; + if (isset($this->aliases[$charset])) + $charset = $this->aliases[$charset]; $this->charset = $charset; diff --git a/program/localization/en_GB/labels.inc b/program/localization/en_GB/labels.inc index 8d5509b64..581a7171a 100644 --- a/program/localization/en_GB/labels.inc +++ b/program/localization/en_GB/labels.inc @@ -397,6 +397,7 @@ $labels['pagesize'] = 'Rows per page'; $labels['signature'] = 'Signature'; $labels['dstactive'] = 'Summer time'; $labels['showinextwin'] = 'Open message in a new window'; +$labels['showemail'] = 'Show email address with display name'; $labels['composeextwin'] = 'Compose in a new window'; $labels['htmleditor'] = 'Compose HTML messages'; $labels['htmlonreply'] = 'on reply to HTML message only'; diff --git a/program/localization/en_US/csv2vcard.inc b/program/localization/en_US/csv2vcard.inc index 5412f7e20..e7b86795b 100644 --- a/program/localization/en_US/csv2vcard.inc +++ b/program/localization/en_US/csv2vcard.inc @@ -91,3 +91,20 @@ $map['work_phone'] = "Work Phone"; $map['work_address'] = "Work Address"; $map['work_country'] = "Work Country"; $map['work_zipcode'] = "Work ZipCode"; + +// Atmail +$map['date_of_birth'] = "Date of Birth"; +$map['email'] = "Email"; +$map['home_mobile'] = "Home Mobile"; +$map['home_zip'] = "Home Zip"; +$map['info'] = "Info"; +$map['user_photo'] = "User Photo"; +$map['url'] = "URL"; +$map['work_city'] = "Work City"; +$map['work_company'] = "Work Company"; +$map['work_dept'] = "Work Dept"; +$map['work_fax'] = "Work Fax"; +$map['work_mobile'] = "Work Mobile"; +$map['work_state'] = "Work State"; +$map['work_title'] = "Work Title"; +$map['work_zip'] = "Work Zip"; diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 3e1bde0a5..ab57007dd 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -402,6 +402,7 @@ $labels['htmleditor'] = 'Compose HTML messages'; $labels['htmlonreply'] = 'on reply to HTML message'; $labels['htmlonreplyandforward'] = 'on forward or reply to HTML message'; $labels['htmlsignature'] = 'HTML signature'; +$labels['showemail'] = 'Show email address with display name'; $labels['previewpane'] = 'Show preview pane'; $labels['skin'] = 'Interface skin'; $labels['logoutclear'] = 'Clear Trash on logout'; diff --git a/program/localization/fr_FR/csv2vcard.inc b/program/localization/fr_FR/csv2vcard.inc new file mode 100644 index 000000000..bb77001b5 --- /dev/null +++ b/program/localization/fr_FR/csv2vcard.inc @@ -0,0 +1,96 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | localization/<lang>/csv2vcard.inc | + | | + | Localization file of the Roundcube Webmail client | + | Copyright (C) 2005-2013, 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. | + | | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <alec@alec.pl> | + +-----------------------------------------------------------------------+ +*/ + +// This is a list of CSV column names specified in CSV file header +// These must be original texts used in Outlook/Thunderbird exported csv files +// Encoding UTF-8 + +$map = array(); + +// MS Outlook 2010 +$map['anniversary'] = "Anniversaire de mariage ou fête"; +$map['assistants_name'] = "Nom de l''assistant(e)"; +$map['assistants_phone'] = "Téléphone de l''assistant(e)"; +$map['birthday'] = "Anniversaire"; +$map['business_city'] = "Ville (bureau)"; +$map['business_countryregion'] = "Pays/Région (bureau)"; +$map['business_fax'] = "Télécopie (bureau)"; +$map['business_phone'] = "Téléphone (bureau)"; +$map['business_phone_2'] = "Téléphone 2 (bureau)"; +$map['business_postal_code'] = "Code postal (bureau)"; +$map['business_state'] = "Dép/Région (bureau)"; +$map['business_street'] = "Rue (bureau)"; +$map['car_phone'] = "Téléphone (voiture)"; +$map['categories'] = "Catégories"; +$map['company'] = "Société"; +$map['department'] = "Service"; +$map['email_address'] = "Adresse de messagerie"; +$map['first_name'] = "Prénom"; +$map['gender'] = "Sexe"; +$map['home_city'] = "Ville (domicile)"; +$map['home_countryregion'] = "Pays/Région (domicile)"; +$map['home_fax'] = "Télécopie (domicile)"; +$map['home_phone'] = "Téléphone (domicile)"; +$map['home_phone_2'] = "Téléphone 2 (domicile)"; +$map['home_postal_code'] = "Code postal (domicile)"; +$map['home_state'] = "Dép/Région (domicile)"; +$map['home_street'] = "Rue (domicile)"; +$map['job_title'] = "Profession"; +$map['last_name'] = "Nom"; +$map['managers_name'] = "Manager's Name"; +$map['middle_name'] = "Deuxième prénom"; +$map['mobile_phone'] = "Tél. mobile"; +$map['notes'] = "Notes"; +$map['other_city'] = "Ville (autre)"; +$map['other_countryregion'] = "Pays/Région (autre)"; +$map['other_fax'] = "Télécopie (autre)"; +$map['other_phone'] = "Téléphone (autre)"; +$map['other_postal_code'] = "Code postal (autre)"; +$map['other_state'] = "Dép/Région (autre)"; +$map['other_street'] = "Rue (autre)"; +$map['pager'] = "Récepteur de radiomessagerie"; +$map['primary_phone'] = "Téléphone principal"; +$map['spouse'] = "Conjoint(e)"; +$map['suffix'] = "Suffixe"; +$map['title'] = "Titre"; +$map['web_page'] = "Page Web"; + +// Thunderbird +$map['birth_day'] = "Jour"; +$map['birth_month'] = "Mois"; +$map['birth_year'] = "Année de naissance"; +$map['display_name'] = "Nom à afficher"; +$map['fax_number'] = "Fax"; +$map['home_address'] = "Adresse privée"; +$map['home_country'] = "Région"; +$map['home_zipcode'] = "Code postal"; +$map['mobile_number'] = "Portable"; +$map['nickname'] = "Surnom"; +$map['organization'] = "Société"; +$map['pager_number'] = "Pager"; +$map['primary_email'] = "Adresse électronique principale"; +$map['secondary_email'] = "Adresse électronique secondaire"; +$map['web_page_1'] = "Site Web 1"; +$map['web_page_2'] = "Site Web 2"; +$map['work_phone'] = "Tél. professionnel"; +$map['work_address'] = "Adresse professionnelle"; +$map['work_country'] = "Région"; +$map['work_zipcode'] = "Code postal"; + +// Other +$map['_home_city'] = "Ville"; diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index ffc0b3b92..8faf76f78 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -167,7 +167,7 @@ function rcmail_set_sourcename($abook) // get address book name (for display) if ($abook && $_SESSION['addressbooks_count'] > 1) { $name = $abook->get_name(); - if (!$name && $source == 0) { + if (!$name) { $name = rcube_label('personaladrbook'); } $OUTPUT->set_env('sourcename', html_entity_decode($name, ENT_COMPAT, 'UTF-8')); @@ -183,7 +183,6 @@ function rcmail_directory_list($attrib) $attrib['id'] = 'rcmdirectorylist'; $out = ''; - $local_id = '0'; $jsdata = array(); $line_templ = html::tag('li', array( @@ -270,7 +269,6 @@ function rcmail_contact_groups($args) $groups_html = ''; $groups = $RCMAIL->get_address_book($args['source'])->list_groups(); - $js_id = $RCMAIL->JQ($args['source']); if (!empty($groups)) { $line_templ = html::tag('li', array( @@ -283,7 +281,6 @@ function rcmail_contact_groups($args) $is_collapsed = strpos($RCMAIL->config->get('collapsed_abooks',''), '&'.rawurlencode($args['source']).'&') !== false; $args['out'] .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), ' '); - $jsdata = array(); foreach ($groups as $group) { $groups_html .= sprintf($line_templ, rcube_utils::html_identifier('G' . $args['source'] . $group['ID'], true), @@ -297,7 +294,7 @@ function rcmail_contact_groups($args) } $args['out'] .= html::tag('ul', - array('class' => 'groups', 'style' => ($is_collapsed ? "display:none;" : null)), + array('class' => 'groups', 'style' => ($is_collapsed || empty($groups) ? "display:none;" : null)), $groups_html); return $args; diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 72da15078..915aac884 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -88,7 +88,7 @@ function rcmail_import_confirm($attrib) $content = html::p(null, rcube_label(array( 'name' => 'importconfirm', - 'nr' => $IMORT_STATS->inserted, + 'nr' => $IMPORT_STATS->inserted, 'vars' => $vars, )) . ($IMPORT_STATS->names ? ':' : '.')); @@ -98,7 +98,7 @@ function rcmail_import_confirm($attrib) if ($IMPORT_STATS->skipped) { $content .= html::p(null, rcube_label(array( 'name' => 'importconfirmskipped', - 'nr' => $IMORT_STATS->skipped, + 'nr' => $IMPORT_STATS->skipped, 'vars' => $vars, )) . ':'); $content .= html::p('em', join(', ', array_map('Q', $IMPORT_STATS->skipped_names))); diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc index 16be89f94..1a97c65b1 100644 --- a/program/steps/addressbook/show.inc +++ b/program/steps/addressbook/show.inc @@ -101,8 +101,6 @@ function rcmail_contact_head($attrib) return false; } - $microformats = array('name' => 'fn', 'email' => 'email'); - $form = array( 'head' => array( // section 'head' is magic! 'content' => array( @@ -177,7 +175,7 @@ function rcmail_contact_details($attrib) } -function rcmail_render_email_value($email, $col) +function rcmail_render_email_value($email) { return html::a(array( 'href' => 'mailto:' . $email, @@ -188,7 +186,7 @@ function rcmail_render_email_value($email, $col) } -function rcmail_render_url_value($url, $col) +function rcmail_render_url_value($url) { $prefix = preg_match('!^(http|ftp)s?://!', $url) ? '' : 'http://'; return html::a(array( @@ -223,7 +221,7 @@ function rcmail_contact_record_groups($contact_id) } $hiddenfields = new html_hiddenfield(array('name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); - $hiddenfields->add(array('name' => '_cid', 'value' => $record['ID'])); + $hiddenfields->add(array('name' => '_cid', 'value' => $contact_id)); $form_start = $RCMAIL->output->request_form(array( 'name' => "form", 'method' => "post", diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc index 55579814c..f9e8d71a4 100644 --- a/program/steps/mail/autocomplete.inc +++ b/program/steps/mail/autocomplete.inc @@ -102,7 +102,7 @@ if (!empty($book_types) && strlen($search)) { // also list matching contact groups if ($abook->groups && count($contacts) < $MAXNUM) { - foreach ($abook->list_groups($search) as $group) { + foreach ($abook->list_groups($search, $mode) as $group) { $abook->reset(); $abook->set_group($group['ID']); $group_prop = $abook->get_group($group['ID']); diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 81b598377..c3fc715a6 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -327,6 +327,19 @@ foreach ($parts as $header) { $fvalue .= $v; if ($v = $MESSAGE->headers->cc) $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + if ($v = $MESSAGE->headers->get('Sender', false)) + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + + // When To: and Reply-To: are the same we add From: address to the list (#1489037) + if ($v = $MESSAGE->headers->from) { + $from = rcube_mime::decode_address_list($v, null, false, $MESSAGE->headers->charset, true); + $to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, false, $MESSAGE->headers->charset, true); + $replyto = rcube_mime::decode_address_list($MESSAGE->headers->replyto, null, false, $MESSAGE->headers->charset, true); + + if (count($replyto) && !count(array_diff($to, $replyto)) && count(array_diff($from, $to))) { + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + } + } } } else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { @@ -386,7 +399,7 @@ function rcmail_compose_headers($attrib) { global $MESSAGE; - list($form_start, $form_end) = get_form_tags($attrib); + list($form_start,) = get_form_tags($attrib); $out = ''; $part = strtolower($attrib['part']); @@ -450,7 +463,7 @@ function rcmail_compose_headers($attrib) function rcmail_compose_header_from($attrib) { - global $MESSAGE, $OUTPUT, $RCMAIL, $compose_mode; + global $MESSAGE, $OUTPUT, $RCMAIL, $COMPOSE, $compose_mode; // pass the following attributes to the form class $field_attrib = array('name' => '_from'); @@ -553,7 +566,7 @@ function rcmail_message_is_html() function rcmail_prepare_message_body() { - global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $LINE_LENGTH, $HTML_MODE; + global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $HTML_MODE; // use posted message body if (!empty($_POST['_message'])) { @@ -626,7 +639,7 @@ function rcmail_prepare_message_body() function rcmail_compose_part_body($part, $isHtml = false) { - global $RCMAIL, $MESSAGE, $compose_mode; + global $RCMAIL, $MESSAGE, $LINE_LENGTH, $compose_mode; // Check if we have enough memory to handle the message in it // #1487424: we need up to 10x more memory than the body @@ -702,7 +715,7 @@ function rcmail_compose_part_body($part, $isHtml = false) function rcmail_compose_body($attrib) { - global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE, $MESSAGE_BODY; + global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $HTML_MODE, $MESSAGE_BODY; list($form_start, $form_end) = get_form_tags($attrib); unset($attrib['form']); @@ -886,8 +899,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml) if (!isset($COMPOSE['forward_attachments']) && is_array($MESSAGE->mime_parts)) $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml); - $date = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')); - $charset = $RCMAIL->output->get_charset(); + $date = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')); if (!$bodyIsHtml) { $prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n"; @@ -941,7 +953,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml) function rcmail_create_draft_body($body, $bodyIsHtml) { - global $MESSAGE, $OUTPUT, $COMPOSE; + global $MESSAGE, $COMPOSE; /** * add attachments @@ -989,7 +1001,7 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) global $RCMAIL, $COMPOSE, $compose_mode; $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } @@ -1076,7 +1088,7 @@ function rcmail_write_forward_attachments() $names = array(); $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } @@ -1211,10 +1223,11 @@ function rcmail_save_image($path, $mimetype='') // handle attachments in memory $data = file_get_contents($path); + $name = rcmail_basename($path); $attachment = array( 'group' => $COMPOSE['id'], - 'name' => rcmail_basename($path), + 'name' => $name, 'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name), 'data' => $data, 'size' => strlen($data), @@ -1484,7 +1497,7 @@ function rcmail_editor_selector($attrib) $select->add(Q(rcube_label('plaintoggle')), 'plain'); return $select->show($useHtml ? 'html' : 'plain'); - +/* foreach ($choices as $value => $text) { $attrib['id'] = '_' . $value; $attrib['value'] = $value; @@ -1492,6 +1505,7 @@ function rcmail_editor_selector($attrib) } return $selector; +*/ } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 60db3f310..17ab6f97d 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -224,7 +224,7 @@ function rcmail_message_list($attrib) if (!in_array('threads', $a_show_cols)) array_unshift($a_show_cols, 'threads'); - $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path']; + $_SESSION['skin_path'] = $CONFIG['skin_path']; // set client env $OUTPUT->add_gui_object('messagelist', $attrib['id']); @@ -236,15 +236,13 @@ function rcmail_message_list($attrib) $OUTPUT->include_script('list.js'); - $thead = ''; - foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) - $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); + $table = new html_table($attrib); + if (!$attrib['noheader']) { + foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) + $table->add_header(array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); + } - return html::tag('table', - $attrib, - html::tag('thead', null, html::tag('tr', null, $thead)) . - html::tag('tbody', null, ''), - array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); + return $table->show(); } @@ -291,7 +289,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL; // get name of smart From/To column in folder context - if (($f = array_search('fromto', $a_show_cols)) !== false) { + if (array_search('fromto', $a_show_cols) !== false) { $smart_col = rcmail_message_list_smart_column_name(); } @@ -307,7 +305,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null } // loop through message headers - foreach ($a_headers as $n => $header) { + foreach ($a_headers as $header) { if (empty($header)) continue; @@ -381,7 +379,6 @@ function rcmail_message_list_head($attrib, $a_show_cols) global $RCMAIL; $skin_path = $_SESSION['skin_path']; - $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s")); // check to see if we have some settings for sorting $sort_col = $_SESSION['sort_col']; @@ -417,7 +414,7 @@ function rcmail_message_list_head($attrib, $a_show_cols) $cells = array(); // get name of smart From/To column in folder context - if (($f = array_search('fromto', $a_show_cols)) !== false) { + if (array_search('fromto', $a_show_cols) !== false) { $smart_col = rcmail_message_list_smart_column_name(); } @@ -897,7 +894,7 @@ function rcmail_washtml_callback($tagname, $attrib, $content, $washtml) */ function rcmail_message_headers($attrib, $headers=null) { - global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL; + global $MESSAGE, $PRINT_MODE, $RCMAIL; static $sa_attrib; // keep header table attrib @@ -1082,7 +1079,7 @@ function rcmail_message_body($attrib) $header_attrib[$regs[1]] = $value; if (!empty($MESSAGE->parts)) { - foreach ($MESSAGE->parts as $i => $part) { + foreach ($MESSAGE->parts as $part) { if ($part->type == 'headers') { $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers)); } @@ -1440,7 +1437,8 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $c = count($a_parts); $j = 0; $out = ''; - $allvalues = array(); + $allvalues = array(); + $show_email = $RCMAIL->config->get('message_show_email'); if ($addicon && !isset($_SESSION['writeable_abook'])) { $_SESSION['writeable_abook'] = $RCMAIL->get_address_sources(true) ? true : false; @@ -1453,7 +1451,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $string = $part['string']; // phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>" - if ($name && $name != $mailto && strpos($name, '@')) { + if (!$show_email && $name && $name != $mailto && strpos($name, '@')) { $name = ''; } @@ -1471,13 +1469,21 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, } else if (check_email($part['mailto'], false)) { if ($linked) { - $address = html::a(array( - 'href' => 'mailto:'.$mailto, - 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), - 'title' => $mailto, - 'class' => "rcmContactAddress", - ), - Q($name ? $name : $mailto)); + $attrs = array( + 'href' => 'mailto:' . $mailto, + 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), + 'class' => "rcmContactAddress", + ); + + if ($show_email && $name && $mailto) { + $content = Q($name ? sprintf('%s <%s>', $name, $mailto) : $mailto); + } + else { + $content = Q($name ? $name : $mailto); + $attrs['title'] = $mailto; + } + + $address = html::a($attrs, $content); } else { $address = html::span(array('title' => $mailto, 'class' => "rcmContactAddress"), @@ -1730,8 +1736,7 @@ function rcmail_send_mdn($message, &$smtp_error) $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options); - if ($sent) - { + if ($sent) { $RCMAIL->storage->set_flag($message->uid, 'MDNSENT'); return true; } diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 1947c0f29..2ad1ba9bd 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -228,11 +228,11 @@ function rcmail_remote_objects_msg() function rcmail_message_buttons() { - global $MESSAGE, $RCMAIL, $CONFIG; + global $RCMAIL; $mbox = $RCMAIL->storage->get_folder(); $delim = $RCMAIL->storage->get_hierarchy_delimiter(); - $dbox = $CONFIG['drafts_mbox']; + $dbox = $RCMAIL->config->get('drafts_mbox'); // the message is not a draft if ($mbox != $dbox && strpos($mbox, $dbox.$delim) !== 0) { diff --git a/program/steps/settings/edit_prefs.inc b/program/steps/settings/edit_prefs.inc index 971ed60b6..468e4994d 100644 --- a/program/steps/settings/edit_prefs.inc +++ b/program/steps/settings/edit_prefs.inc @@ -40,7 +40,7 @@ function rcmail_user_prefs_form($attrib) $out = $form_start; - foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $idx => $block) { + foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $block) { if (!empty($block['options'])) { $table = new html_table(array('cols' => 2)); diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index 0c7d9063f..34a72dd95 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -283,7 +283,6 @@ function rcube_subscription_form($attrib) $noselect = false; $classes = array($i%2 ? 'even' : 'odd'); - $folder_js = Q($folder['id']); $folder_utf8 = rcube_charset_convert($folder['id'], 'UTF7-IMAP'); $display_folder = str_repeat(' ', $folder['level']) . Q($protected ? rcmail_localize_foldername($folder['id']) : $folder['name']); @@ -394,7 +393,7 @@ function rcmail_rename_folder($oldname, $newname) $a_threaded = (array) $RCMAIL->config->get('message_threading', array()); $oldprefix = '/^' . preg_quote($oldname . $delimiter, '/') . '/'; - foreach ($a_threaded as $key => $val) { + foreach (array_keys($a_threaded) as $key) { if ($key == $oldname) { unset($a_threaded[$key]); $a_threaded[$newname] = true; diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 319c58db9..860f36c35 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -418,6 +418,17 @@ function rcmail_user_prefs($current=null) ); } + // show checkbox to show email instead of name + if (!isset($no_override['message_show_email'])) { + $field_id = 'rcmfd_message_show_email'; + $input_msgshowemail = new html_checkbox(array('name' => '_message_show_email', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['message_show_email'] = array( + 'title' => html::label($field_id, Q(rcube_label('showemail'))), + 'content' => $input_msgshowemail->show($config['message_show_email']?1:0), + ); + } + // show checkbox for HTML/plaintext messages if (!isset($no_override['prefer_html'])) { $field_id = 'rcmfd_htmlmsg'; diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index dfb2b13ac..3bb82aa38 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -60,6 +60,7 @@ switch ($CURR_SECTION) case 'mailview': $a_user_prefs = array( 'message_extwin' => intval($_POST['_message_extwin']), + 'message_show_email' => isset($_POST['_message_show_email']) ? TRUE : FALSE, 'prefer_html' => isset($_POST['_prefer_html']) ? TRUE : FALSE, 'inline_images' => isset($_POST['_inline_images']) ? TRUE : FALSE, 'show_images' => isset($_POST['_show_images']) ? intval($_POST['_show_images']) : 0, diff --git a/program/steps/utils/spell.inc b/program/steps/utils/spell.inc index a0dd35d27..38e4ca285 100644 --- a/program/steps/utils/spell.inc +++ b/program/steps/utils/spell.inc @@ -42,6 +42,13 @@ else { $result = $spellchecker->get_xml(); } +if ($err = $spellchecker->error()) { + rcube::raise_error(array('code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => sprintf("Spell check engine error: " . $err)), + true, false); +} + // set response length header("Content-Length: " . strlen($result)); diff --git a/program/steps/utils/spell_html.inc b/program/steps/utils/spell_html.inc index 861e4ba48..96b41e230 100644 --- a/program/steps/utils/spell_html.inc +++ b/program/steps/utils/spell_html.inc @@ -46,6 +46,11 @@ else if ($request['method'] == 'learnWord') { } if ($error = $spellchecker->error()) { + rcube::raise_error(array('code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => sprintf("Spell check engine error: " . $error)), + true, false); + echo '{"error":{"errstr":"' . addslashes($error) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'; exit; } |