diff options
-rw-r--r-- | CHANGELOG | 2 | ||||
-rw-r--r-- | program/include/main.inc | 32 | ||||
-rw-r--r-- | program/include/rcube_imap.php | 72 | ||||
-rw-r--r-- | program/include/rcube_imap_generic.php | 62 | ||||
-rw-r--r-- | program/localization/en_US/messages.inc | 3 | ||||
-rw-r--r-- | program/localization/pl_PL/messages.inc | 3 | ||||
-rw-r--r-- | program/steps/mail/copy.inc | 4 | ||||
-rw-r--r-- | program/steps/mail/folders.inc | 82 | ||||
-rw-r--r-- | program/steps/mail/list.inc | 3 | ||||
-rw-r--r-- | program/steps/mail/mark.inc | 2 | ||||
-rw-r--r-- | program/steps/mail/move_del.inc | 4 | ||||
-rw-r--r-- | program/steps/mail/search.inc | 3 | ||||
-rw-r--r-- | program/steps/settings/folders.inc | 15 |
13 files changed, 220 insertions, 67 deletions
@@ -16,6 +16,8 @@ CHANGELOG Roundcube Webmail - Improve performance of moving or copying of all messages in a folder - Fix plaintext versions of HTML messages don't contain placeholders for emotions (#1485206) - Improve performance of folder rename and delete actions +- Better support for READ-ONLY and NOPERM responses handling (#1487083) +- Add confirmation message on purge/expunge command response RELEASE 0.5-BETA ---------------- diff --git a/program/include/main.inc b/program/include/main.inc index ad0bccd48..160c835c9 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -1616,6 +1616,38 @@ function rcmail_quota_content($attrib=NULL) /** + * Outputs error message according to server error/response codes + * + * @param string Fallback message label + * @param string Fallback message label arguments + * + * @return void + */ +function rcmail_display_server_error($fallback=null, $fallback_args=null) +{ + global $RCMAIL; + + $err_code = $RCMAIL->imap->get_error_code(); + $res_code = $RCMAIL->imap->get_response_code(); + + if ($res_code == rcube_imap::NOPERM) { + $RCMAIL->output->show_message('errornoperm', 'error'); + } + else if ($res_code == rcube_imap::READONLY) { + $RCMAIL->output->show_message('errorreadonly', 'error'); + } + else if ($err_code && ($err_str = $RCMAIL->imap->get_error_str())) { + $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); + } + else if ($fallback) { + $RCMAIL->output->show_message($fallback, 'error', $fallback_args); + } + + return true; +} + + +/** * Output HTML editor scripts * * @param string Editor mode diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index f0d11194c..d1947c4a3 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -100,6 +100,16 @@ class rcube_imap 'RETURN-PATH', ); + const UNKNOWN = 0; + const NOPERM = 1; + const READONLY = 2; + const TRYCREATE = 3; + const INUSE = 4; + const OVERQUOTA = 5; + const ALREADYEXISTS = 6; + const NONEXISTENT = 7; + const CONTACTADMIN = 8; + /** * Object constructor @@ -220,7 +230,51 @@ class rcube_imap */ function get_error_str() { - return ($this->conn) ? $this->conn->error : ''; + return ($this->conn) ? $this->conn->error : null; + } + + + /** + * Returns code of last command response + * + * @return int Response code + */ + function get_response_code() + { + if (!$this->conn) + return self::UNKNOWN; + + switch ($this->conn->resultcode) { + case 'NOPERM': + return self::NOPERM; + case 'READ-ONLY': + return self::READONLY; + case 'TRYCREATE': + return self::TRYCREATE; + case 'INUSE': + return self::INUSE; + case 'OVERQUOTA': + return self::OVERQUOTA; + case 'ALREADYEXISTS': + return self::ALREADYEXISTS; + case 'NONEXISTENT': + return self::NONEXISTENT; + case 'CONTACTADMIN': + return self::CONTACTADMIN; + default: + return self::UNKNOWN; + } + } + + + /** + * Returns last command response + * + * @return string Response + */ + function get_response_str() + { + return ($this->conn) ? $this->conn->result : null; } @@ -295,9 +349,9 @@ class rcube_imap * @param string $mailbox Mailbox/Folder name * @access public */ - function select_mailbox($mailbox) + function select_mailbox($mailbox=null) { - $mailbox = $this->mod_mailbox($mailbox); + $mailbox = strlen($mailbox) ? $this->mod_mailbox($mailbox) : $this->mailbox; $selected = $this->conn->select($mailbox); @@ -2769,6 +2823,18 @@ class rcube_imap else $a_uids = NULL; + // force mailbox selection and check if mailbox is writeable + // to prevent a situation when CLOSE is executed on closed + // or EXPUNGE on read-only mailbox + $result = $this->conn->select($mailbox); + if (!$result) { + return false; + } + if (!$this->conn->data['READ-WRITE']) { + $this->conn->setError(rcube_imap_generic::ERROR_READONLY, "Mailbox is read-only"); + return false; + } + // CLOSE(+SELECT) should be faster than EXPUNGE if (empty($a_uids) || $a_uids == '1:*') $result = $this->conn->close(); diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index 10bc6d06b..b2346ba13 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -85,6 +85,8 @@ class rcube_imap_generic { public $error; public $errornum; + public $result; + public $resultcode; public $data = array(); public $flags = array( 'SEEN' => '\\Seen', @@ -112,8 +114,9 @@ class rcube_imap_generic const ERROR_NO = -1; const ERROR_BAD = -2; const ERROR_BYE = -3; - const ERROR_COMMAND = -5; const ERROR_UNKNOWN = -4; + const ERROR_COMMAND = -5; + const ERROR_READONLY = -6; const COMMAND_NORESPONSE = 1; const COMMAND_CAPABILITY = 2; @@ -302,7 +305,7 @@ class rcube_imap_generic $str = trim($matches[2]); if ($res == 'OK') { - return $this->errornum = self::ERROR_OK; + $this->errornum = self::ERROR_OK; } else if ($res == 'NO') { $this->errornum = self::ERROR_NO; } else if ($res == 'BAD') { @@ -313,15 +316,29 @@ class rcube_imap_generic $this->errornum = self::ERROR_BYE; } - if ($str) - $this->error = $err_prefix ? $err_prefix.$str : $str; + if ($str) { + $str = trim($str); + // get response string and code (RFC5530) + if (preg_match("/^\[([a-z-]+)\]/i", $str, $m)) { + $this->resultcode = strtoupper($m[1]); + $str = trim(substr($str, strlen($m[1]) + 2)); + } + else { + $this->resultcode = null; + } + $this->result = $str; + + if ($this->errornum != self::ERROR_OK) { + $this->error = $err_prefix ? $err_prefix.$str : $str; + } + } return $this->errornum; } return self::ERROR_UNKNOWN; } - private function setError($code, $msg='') + function setError($code, $msg='') { $this->errornum = $code; $this->error = $msg; @@ -838,6 +855,8 @@ class rcube_imap_generic } } + $this->data['READ-WRITE'] = $this->resultcode != 'READ-ONLY'; + $this->selected = $mailbox; return true; } @@ -906,6 +925,11 @@ class rcube_imap_generic return false; } + if (!$this->data['READ-WRITE']) { + $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'EXPUNGE'); + return false; + } + // Clear internal status cache unset($this->data['STATUS:'.$mailbox]); @@ -1001,11 +1025,15 @@ class rcube_imap_generic { $num_in_trash = $this->countMessages($mailbox); if ($num_in_trash > 0) { - $this->delete($mailbox, '1:*'); + $res = $this->delete($mailbox, '1:*'); } - $res = $this->close(); -// $res = $this->expunge($mailbox); + if ($res) { + if ($this->selected == $mailbox) + $res = $this->close(); + else + $res = $this->expunge($mailbox); + } return $res; } @@ -1715,6 +1743,11 @@ class rcube_imap_generic return false; } + if (!$this->data['READ-WRITE']) { + $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + return false; + } + // Clear internal status cache if ($flag == 'SEEN') { unset($this->data['STATUS:'.$mailbox]['UNSEEN']); @@ -1758,6 +1791,15 @@ class rcube_imap_generic function move($messages, $from, $to) { + if (!$this->select($from)) { + return false; + } + + if (!$this->data['READ-WRITE']) { + $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + return false; + } + $r = $this->copy($messages, $from, $to); if ($r) { @@ -2963,9 +3005,9 @@ class rcube_imap_generic $this->parseCapability($matches[1], true); } - // return last line only (without command tag and result) + // return last line only (without command tag, result and response code) if ($line && ($options & self::COMMAND_LASTLINE)) { - $response = preg_replace("/^$tag (OK|NO|BAD|BYE|PREAUTH)?\s*/i", '', trim($line)); + $response = preg_replace("/^$tag (OK|NO|BAD|BYE|PREAUTH)?\s*(\[[a-z-]+\])?\s*/i", '', trim($line)); } return $noresp ? $code : array($code, $response); diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc index d47f555f9..df78b0f34 100644 --- a/program/localization/en_US/messages.inc +++ b/program/localization/en_US/messages.inc @@ -24,6 +24,8 @@ $messages['sessionerror'] = 'Your session is invalid or expired'; $messages['imaperror'] = 'Connection to IMAP server failed'; $messages['servererror'] = 'Server Error!'; $messages['servererrormsg'] = 'Server Error: $msg'; +$messages['errorreadonly'] = 'Unable to perform operation. Folder is read-only'; +$messages['errornoperm'] = 'Unable to perform operation. Permission denied'; $messages['invalidrequest'] = 'Invalid request! No data was saved.'; $messages['nomessagesfound'] = 'No messages found in this mailbox'; $messages['loggedout'] = 'You have successfully terminated the session. Good bye!'; @@ -81,6 +83,7 @@ $messages['folderdeleted'] = 'Folder successfully deleted'; $messages['foldersubscribed'] = 'Folder successfully subscribed'; $messages['folderunsubscribed'] = 'Folder successfully unsubscribed'; $messages['folderpurged'] = 'Folder successfully purged'; +$messages['folderexpunged'] = 'Folder successfully emptied'; $messages['deletedsuccessfully'] = 'Successfully deleted'; $messages['converting'] = 'Removing formatting...'; $messages['messageopenerror'] = 'Could not load message from server'; diff --git a/program/localization/pl_PL/messages.inc b/program/localization/pl_PL/messages.inc index 128163af3..d3c205bf0 100644 --- a/program/localization/pl_PL/messages.inc +++ b/program/localization/pl_PL/messages.inc @@ -135,9 +135,12 @@ $messages['folderunsubscribing'] = 'Odsubskrybowanie folderu...'; $messages['foldersubscribed'] = 'Folder pomyślnie zasubskrybowany'; $messages['folderunsubscribed'] = 'Folder pomyślnie odsubskrybowany'; $messages['folderpurged'] = 'Folder pomyślnie wyczyszczony'; +$messages['folderexpunged'] = 'Folder pomyślnie opróżniony'; $messages['namecannotbeempty'] = 'Nazwa nie może być pusta'; $messages['nametoolong'] = 'Name jest zbyt długa'; $messages['folderupdated'] = 'Folder pomyślnie zaktualizowany'; $messages['foldercreated'] = 'Folder pomyślnie utworzony'; +$messages['errorreadonly'] = 'Nie można wykonać operacji. Folder tylko do odczytu'; +$messages['errornoperm'] = 'Nie można wykonać operacji. Brak uprawnień'; ?> diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc index 4cd51d816..8a7c5916b 100644 --- a/program/steps/mail/copy.inc +++ b/program/steps/mail/copy.inc @@ -33,7 +33,7 @@ if (!empty($_POST['_uid']) && !empty($_POST['_target_mbox'])) { if (!$copied) { // send error message - $OUTPUT->show_message('errorcopying', 'error'); + rcmail_display_server_error('errorcopying'); $OUTPUT->send(); exit; } @@ -52,5 +52,3 @@ else { // send response $OUTPUT->send(); - - diff --git a/program/steps/mail/folders.inc b/program/steps/mail/folders.inc index a74e61615..3b96dc29c 100644 --- a/program/steps/mail/folders.inc +++ b/program/steps/mail/folders.inc @@ -20,55 +20,61 @@ // only process ajax requests if (!$OUTPUT->ajax_call) - return; + return; $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); // send EXPUNGE command -if ($RCMAIL->action=='expunge') -{ - $success = $IMAP->expunge($mbox); +if ($RCMAIL->action == 'expunge') { - // reload message list if current mailbox - if ($success && !empty($_REQUEST['_reload'])) - { - $OUTPUT->command('set_quota', rcmail_quota_content()); - $OUTPUT->command('message_list.clear'); - $RCMAIL->action = 'list'; - return; - } - else - $commands = "// expunged: $success\n"; + $success = $IMAP->expunge($mbox); + + // reload message list if current mailbox + if ($success) { + $OUTPUT->show_message('folderexpunged', 'confirmation'); + + if (!empty($_REQUEST['_reload'])) { + $OUTPUT->command('set_quota', rcmail_quota_content()); + $OUTPUT->command('message_list.clear'); + $RCMAIL->action = 'list'; + return; + } + } + else { + rcmail_display_server_error(); + } } // clear mailbox -else if ($RCMAIL->action=='purge') +else if ($RCMAIL->action == 'purge') { - $delimiter = $IMAP->get_hierarchy_delimiter(); - $trash_regexp = '/^' . preg_quote($CONFIG['trash_mbox'] . $delimiter, '/') . '/'; - $junk_regexp = '/^' . preg_quote($CONFIG['junk_mbox'] . $delimiter, '/') . '/'; + $delimiter = $IMAP->get_hierarchy_delimiter(); + $trash_regexp = '/^' . preg_quote($CONFIG['trash_mbox'] . $delimiter, '/') . '/'; + $junk_regexp = '/^' . preg_quote($CONFIG['junk_mbox'] . $delimiter, '/') . '/'; + + // we should only be purging trash and junk (or their subfolders) + if ($mbox == $CONFIG['trash_mbox'] || $mbox == $CONFIG['junk_mbox'] + || preg_match($trash_regexp, $mbox) || preg_match($junk_regexp, $mbox) + ) { + $success = $IMAP->clear_mailbox($mbox); - // we should only be purging trash and junk (or their subfolders) - if ($mbox == $CONFIG['trash_mbox'] || $mbox == $CONFIG['junk_mbox'] - || preg_match($trash_regexp, $mbox) || preg_match($junk_regexp, $mbox)) - { - $success = $IMAP->clear_mailbox($mbox); + if ($success) { + $OUTPUT->show_message('folderpurged', 'confirmation'); - if ($success && !empty($_REQUEST['_reload'])) - { - $OUTPUT->set_env('messagecount', 0); - $OUTPUT->set_env('pagecount', 0); - $OUTPUT->command('message_list.clear'); - $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text()); - $OUTPUT->command('set_unread_count', $mbox, 0); - $OUTPUT->command('set_quota', rcmail_quota_content()); - rcmail_set_unseen_count($mbox, 0); + if (!empty($_REQUEST['_reload'])) { + $OUTPUT->set_env('messagecount', 0); + $OUTPUT->set_env('pagecount', 0); + $OUTPUT->command('message_list.clear'); + $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text()); + $OUTPUT->command('set_unread_count', $mbox, 0); + $OUTPUT->command('set_quota', rcmail_quota_content()); + rcmail_set_unseen_count($mbox, 0); + } + } + else { + rcmail_display_server_error(); + } } - else - $commands = "// purged: $success"; - } } -$OUTPUT->send($commands); - - +$OUTPUT->send(); diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc index 6353be7cc..7e6d294c8 100644 --- a/program/steps/mail/list.inc +++ b/program/steps/mail/list.inc @@ -106,8 +106,7 @@ if (isset($a_headers) && count($a_headers)) else { // handle IMAP errors (e.g. #1486905) if ($err_code = $IMAP->get_error_code()) { - $err_str = $IMAP->get_error_str(); - $OUTPUT->show_message('servererrormsg', 'error', array('msg' => $err_str)); + rcmail_display_server_error(); } else if ($search_request) $OUTPUT->show_message('searchnomatch', 'notice'); diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc index 65a8fc6ba..1ff4407c5 100644 --- a/program/steps/mail/mark.inc +++ b/program/steps/mail/mark.inc @@ -47,7 +47,7 @@ if (($uids = get_input_value('_uid', RCUBE_INPUT_POST)) && ($flag = get_input_va // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); - $OUTPUT->show_message('errormarking', 'error'); + rcmail_display_server_error('errormarking'); $OUTPUT->send(); exit; } diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc index 06bef0dc6..2db3ec31a 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -39,7 +39,7 @@ if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_targe // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); - $OUTPUT->show_message('errormoving', 'error'); + rcmail_display_server_error('errormoving'); $OUTPUT->send(); exit; } @@ -60,7 +60,7 @@ else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); - $OUTPUT->show_message('errordeleting', 'error'); + rcmail_display_server_error('errordeleting'); $OUTPUT->send(); exit; } diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc index 90d1c374c..39fb32fc9 100644 --- a/program/steps/mail/search.inc +++ b/program/steps/mail/search.inc @@ -124,8 +124,7 @@ if (!empty($result_h)) { } // handle IMAP errors (e.g. #1486905) else if ($err_code = $IMAP->get_error_code()) { - $err_str = $IMAP->get_error_str(); - $OUTPUT->show_message('servererrormsg', 'error', array('msg' => $err_str)); + rcmail_display_server_error(); } else { $OUTPUT->show_message('searchnomatch', 'notice'); diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index 1ceca7416..7ae4fb35d 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -34,7 +34,7 @@ if ($RCMAIL->action == 'subscribe') // Handle virtual (non-existing) folders if (!$result && $IMAP->get_error_code() == -1 && - strpos($IMAP->get_error_str(), '[TRYCREATE]') + $IMAP->get_response_code() == rcube_imap::TRYCREATE ) { $result = $IMAP->create_mailbox($mbox, true); if ($result) { @@ -45,7 +45,7 @@ if ($RCMAIL->action == 'subscribe') if ($result) $OUTPUT->show_message('foldersubscribed', 'confirmation'); else - $OUTPUT->show_message('errorsaving', 'error'); + rcmail_display_server_error('errorsaving'); } } @@ -58,7 +58,7 @@ else if ($RCMAIL->action == 'unsubscribe') if ($result) $OUTPUT->show_message('folderunsubscribed', 'confirmation'); else - $OUTPUT->show_message('errorsaving', 'error'); + rcmail_display_server_error('errorsaving'); } } @@ -92,7 +92,7 @@ else if ($RCMAIL->action == 'delete-folder') $OUTPUT->command('set_quota', rcmail_quota_content()); } else if (!$deleted) { - $OUTPUT->show_message('errorsaving', 'error'); + rcmail_display_server_error('errorsaving'); } } @@ -141,7 +141,7 @@ else if ($RCMAIL->action == 'rename-folder') rcube_charset_convert($name, 'UTF7-IMAP'), $display_rename, $before); } else if (!$rename) { - $OUTPUT->show_message('errorsaving', 'error'); + rcmail_display_server_error('errorsaving'); } } @@ -179,7 +179,7 @@ else if ($RCMAIL->action == 'purge') $OUTPUT->command('show_folder', $mbox_utf8, null, true); } else { - $OUTPUT->show_message('errorsaving', 'error'); + rcmail_display_server_error('errorsaving'); } } @@ -195,6 +195,9 @@ else if ($RCMAIL->action == 'folder-size') if ($size !== false) { $OUTPUT->command('folder_size_update', show_bytes($size)); } + else { + rcmail_display_server_error(); + } } if ($OUTPUT->ajax_call) |