| | |
| | | /** |
| | | * Response parser. |
| | | * |
| | | * @param string $string Response text |
| | | * @param string $err_prefix Error message prefix |
| | | * @param string $string Response text |
| | | * @param string $err_prefix Error message prefix |
| | | * |
| | | * @return int Response status |
| | | */ |
| | | function parseResult($string, $err_prefix='') |
| | | function parseResult($string, $err_prefix = '') |
| | | { |
| | | if (preg_match('/^[a-z0-9*]+ (OK|NO|BAD|BYE)(.*)$/i', trim($string), $matches)) { |
| | | $res = strtoupper($matches[1]); |
| | |
| | | |
| | | if ($res == 'OK') { |
| | | $this->errornum = self::ERROR_OK; |
| | | } else if ($res == 'NO') { |
| | | } |
| | | else if ($res == 'NO') { |
| | | $this->errornum = self::ERROR_NO; |
| | | } else if ($res == 'BAD') { |
| | | } |
| | | else if ($res == 'BAD') { |
| | | $this->errornum = self::ERROR_BAD; |
| | | } else if ($res == 'BYE') { |
| | | } |
| | | else if ($res == 'BYE') { |
| | | $this->closeSocket(); |
| | | $this->errornum = self::ERROR_BYE; |
| | | } |
| | |
| | | $this->data['COPYUID'] = array($m[1], $m[2]); |
| | | } |
| | | } |
| | | |
| | | $this->result = $str; |
| | | |
| | | if ($this->errornum != self::ERROR_OK) { |
| | |
| | | /** |
| | | * Error code/message setter. |
| | | */ |
| | | function setError($code, $msg = '') |
| | | protected function setError($code, $msg = '') |
| | | { |
| | | $this->errornum = $code; |
| | | $this->error = $msg; |
| | |
| | | * |
| | | * @return bool True any check is true or connection is closed. |
| | | */ |
| | | function startsWith($string, $match, $error = false, $nonempty = false) |
| | | protected function startsWith($string, $match, $error = false, $nonempty = false) |
| | | { |
| | | if (!$this->fp) { |
| | | return true; |
| | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Capabilities checker |
| | | */ |
| | | protected function hasCapability($name) |
| | | { |
| | | if (empty($this->capability) || $name == '') { |
| | |
| | | * |
| | | * @return mixed Capability values array for key=value pairs, true/false for others |
| | | */ |
| | | function getCapability($name) |
| | | public function getCapability($name) |
| | | { |
| | | $result = $this->hasCapability($name); |
| | | |
| | |
| | | return $this->hasCapability($name); |
| | | } |
| | | |
| | | function clearCapability() |
| | | /** |
| | | * Clears detected server capabilities |
| | | */ |
| | | public function clearCapability() |
| | | { |
| | | $this->capability = array(); |
| | | $this->capability_readed = false; |
| | |
| | | * |
| | | * @return resource Connection resourse on success, error code on error |
| | | */ |
| | | function authenticate($user, $pass, $type = 'PLAIN') |
| | | protected function authenticate($user, $pass, $type = 'PLAIN') |
| | | { |
| | | if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') { |
| | | if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) { |
| | |
| | | $authc = $user; |
| | | $user = ''; |
| | | } |
| | | |
| | | $auth_sasl = Auth_SASL::factory('digestmd5'); |
| | | $reply = base64_encode($auth_sasl->getResponse($authc, $pass, |
| | | $reply = base64_encode($auth_sasl->getResponse($authc, $pass, |
| | | base64_decode($challenge), $this->host, 'imap', $user)); |
| | | |
| | | // send result |
| | |
| | | $this->putLine(''); |
| | | } |
| | | |
| | | $line = $this->readReply(); |
| | | $line = $this->readReply(); |
| | | $result = $this->parseResult($line); |
| | | } |
| | | elseif ($type == 'GSSAPI') { |
| | | else if ($type == 'GSSAPI') { |
| | | if (!extension_loaded('krb5')) { |
| | | $this->setError(self::ERROR_BYE, |
| | | "The krb5 extension is required for GSSAPI authentication"); |
| | |
| | | |
| | | // send result, get reply and process it |
| | | $this->putLine($reply, true, true); |
| | | $line = $this->readReply(); |
| | | $line = $this->readReply(); |
| | | $result = $this->parseResult($line); |
| | | } |
| | | } |
| | |
| | | /** |
| | | * LOGIN Authentication |
| | | * |
| | | * @param string $user |
| | | * @param string $pass |
| | | * @param string $user Username |
| | | * @param string $pass Password |
| | | * |
| | | * @return resource Connection resourse on success, error code on error |
| | | */ |
| | | function login($user, $password) |
| | | protected function login($user, $password) |
| | | { |
| | | list($code, $response) = $this->execute('LOGIN', array( |
| | | $this->escape($user), $this->escape($password)), self::COMMAND_CAPABILITY | self::COMMAND_ANONYMIZED); |
| | |
| | | * |
| | | * @return string The delimiter |
| | | */ |
| | | function getHierarchyDelimiter() |
| | | public function getHierarchyDelimiter() |
| | | { |
| | | if ($this->prefs['delimiter']) { |
| | | return $this->prefs['delimiter']; |
| | |
| | | * |
| | | * @return array Namespace data hash (personal, other, shared) |
| | | */ |
| | | function getNamespace() |
| | | public function getNamespace() |
| | | { |
| | | if (array_key_exists('namespace', $this->prefs)) { |
| | | return $this->prefs['namespace']; |
| | |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | function connect($host, $user, $password, $options = null) |
| | | public function connect($host, $user, $password, $options = null) |
| | | { |
| | | // configure |
| | | $this->set_prefs($options); |
| | |
| | | * |
| | | * @return bool True if connection is active and user is logged in, False otherwise. |
| | | */ |
| | | function connected() |
| | | public function connected() |
| | | { |
| | | return ($this->fp && $this->logged) ? true : false; |
| | | } |
| | |
| | | /** |
| | | * Closes connection with logout. |
| | | */ |
| | | function closeConnection() |
| | | public function closeConnection() |
| | | { |
| | | if ($this->logged && $this->putLine($this->nextTag() . ' LOGOUT')) { |
| | | $this->readReply(); |
| | |
| | | * |
| | | * @return boolean True on success, false on error |
| | | */ |
| | | function select($mailbox, $qresync_data = null) |
| | | public function select($mailbox, $qresync_data = null) |
| | | { |
| | | if (!strlen($mailbox)) { |
| | | return false; |
| | |
| | | * @return array Status item-value hash |
| | | * @since 0.5-beta |
| | | */ |
| | | function status($mailbox, $items = array()) |
| | | public function status($mailbox, $items = array()) |
| | | { |
| | | if (!strlen($mailbox)) { |
| | | return false; |
| | |
| | | * |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function expunge($mailbox, $messages = null) |
| | | public function expunge($mailbox, $messages = null) |
| | | { |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | |
| | | * @return boolean True on success, False on error |
| | | * @since 0.5 |
| | | */ |
| | | function close() |
| | | public function close() |
| | | { |
| | | $result = $this->execute('CLOSE', null, self::COMMAND_NORESPONSE); |
| | | |
| | |
| | | * |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function subscribe($mailbox) |
| | | public function subscribe($mailbox) |
| | | { |
| | | $result = $this->execute('SUBSCRIBE', array($this->escape($mailbox)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function unsubscribe($mailbox) |
| | | public function unsubscribe($mailbox) |
| | | { |
| | | $result = $this->execute('UNSUBSCRIBE', array($this->escape($mailbox)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | | * Folder creation (CREATE) |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param array $types Optional folder types (RFC 6154) |
| | | * @param array $types Optional folder types (RFC 6154) |
| | | * |
| | | * @return bool True on success, False on error |
| | | */ |
| | | function createFolder($mailbox, $types = null) |
| | | public function createFolder($mailbox, $types = null) |
| | | { |
| | | $args = array($this->escape($mailbox)); |
| | | |
| | |
| | | |
| | | $result = $this->execute('CREATE', $args, self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @return bool True on success, False on error |
| | | */ |
| | | function renameFolder($from, $to) |
| | | public function renameFolder($from, $to) |
| | | { |
| | | $result = $this->execute('RENAME', array($this->escape($from), $this->escape($to)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function deleteFolder($mailbox) |
| | | public function deleteFolder($mailbox) |
| | | { |
| | | $result = $this->execute('DELETE', array($this->escape($mailbox)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function clearFolder($mailbox) |
| | | public function clearFolder($mailbox) |
| | | { |
| | | $num_in_trash = $this->countMessages($mailbox); |
| | | if ($num_in_trash > 0) { |
| | | if ($this->countMessages($mailbox) > 0) { |
| | | $res = $this->flag($mailbox, '1:*', 'DELETED'); |
| | | } |
| | | |
| | | if ($res) { |
| | | if ($this->selected === $mailbox) |
| | | if ($this->selected === $mailbox) { |
| | | $res = $this->close(); |
| | | else |
| | | } |
| | | else { |
| | | $res = $this->expunge($mailbox); |
| | | } |
| | | } |
| | | |
| | | return $res; |
| | |
| | | * @return array|bool List of mailboxes or hash of options if STATUS/MYROGHTS response |
| | | * is requested, False on error. |
| | | */ |
| | | function listMailboxes($ref, $mailbox, $return_opts=array(), $select_opts=array()) |
| | | public function listMailboxes($ref, $mailbox, $return_opts = array(), $select_opts = array()) |
| | | { |
| | | return $this->_listMailboxes($ref, $mailbox, false, $return_opts, $select_opts); |
| | | } |
| | |
| | | * @return array|bool List of mailboxes or hash of options if STATUS/MYROGHTS response |
| | | * is requested, False on error. |
| | | */ |
| | | function listSubscribed($ref, $mailbox, $return_opts = array()) |
| | | public function listSubscribed($ref, $mailbox, $return_opts = array()) |
| | | { |
| | | return $this->_listMailboxes($ref, $mailbox, true, $return_opts, null); |
| | | } |
| | |
| | | // store folder options |
| | | if ($cmd == 'LIST') { |
| | | // Add to options array |
| | | if (empty($this->data['LIST'][$mailbox])) |
| | | if (empty($this->data['LIST'][$mailbox])) { |
| | | $this->data['LIST'][$mailbox] = $opts; |
| | | else if (!empty($opts)) |
| | | } |
| | | else if (!empty($opts)) { |
| | | $this->data['LIST'][$mailbox] = array_unique(array_merge( |
| | | $this->data['LIST'][$mailbox], $opts)); |
| | | } |
| | | } |
| | | } |
| | | else if ($lstatus) { |
| | |
| | | * |
| | | * @return int Number of messages, False on error |
| | | */ |
| | | function countMessages($mailbox) |
| | | public function countMessages($mailbox) |
| | | { |
| | | if ($this->selected === $mailbox && isset($this->data['EXISTS'])) { |
| | | return $this->data['EXISTS']; |
| | |
| | | * |
| | | * @return int Number of messages, False on error |
| | | */ |
| | | function countRecent($mailbox) |
| | | public function countRecent($mailbox) |
| | | { |
| | | if ($this->selected === $mailbox && isset($this->data['RECENT'])) { |
| | | return $this->data['RECENT']; |
| | |
| | | * |
| | | * @return int Number of messages, False on error |
| | | */ |
| | | function countUnseen($mailbox) |
| | | public function countUnseen($mailbox) |
| | | { |
| | | // Check internal cache |
| | | $cache = $this->data['STATUS:'.$mailbox]; |
| | |
| | | * @return array Server identification information key/value hash |
| | | * @since 0.6 |
| | | */ |
| | | function id($items = array()) |
| | | public function id($items = array()) |
| | | { |
| | | if (is_array($items) && !empty($items)) { |
| | | foreach ($items as $key => $value) { |
| | |
| | | * @return array|bool List of enabled extensions, False on error |
| | | * @since 0.6 |
| | | */ |
| | | function enable($extension) |
| | | public function enable($extension) |
| | | { |
| | | if (empty($extension)) { |
| | | return false; |
| | |
| | | * |
| | | * @return rcube_result_index Response data |
| | | */ |
| | | function sort($mailbox, $field = 'ARRIVAL', $criteria = '', $return_uid = false, $encoding = 'US-ASCII') |
| | | public function sort($mailbox, $field = 'ARRIVAL', $criteria = '', $return_uid = false, $encoding = 'US-ASCII') |
| | | { |
| | | $old_sel = $this->selected; |
| | | $supported = array('ARRIVAL', 'CC', 'DATE', 'FROM', 'SIZE', 'SUBJECT', 'TO'); |
| | |
| | | * |
| | | * @return rcube_result_thread Thread data |
| | | */ |
| | | function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII') |
| | | public function thread($mailbox, $algorithm = 'REFERENCES', $criteria = '', $return_uid = false, $encoding = 'US-ASCII') |
| | | { |
| | | $old_sel = $this->selected; |
| | | |
| | |
| | | * |
| | | * @return rcube_result_index Result data |
| | | */ |
| | | function search($mailbox, $criteria, $return_uid=false, $items=array()) |
| | | public function search($mailbox, $criteria, $return_uid = false, $items = array()) |
| | | { |
| | | $old_sel = $this->selected; |
| | | |
| | |
| | | * |
| | | * @return rcube_result_index Response data |
| | | */ |
| | | function index($mailbox, $message_set, $index_field='', $skip_deleted=true, |
| | | public function index($mailbox, $message_set, $index_field='', $skip_deleted=true, |
| | | $uidfetch=false, $return_uid=false) |
| | | { |
| | | $msg_index = $this->fetchHeaderIndex($mailbox, $message_set, |
| | |
| | | return new rcube_result_index($mailbox, $msg_index); |
| | | } |
| | | |
| | | function fetchHeaderIndex($mailbox, $message_set, $index_field='', $skip_deleted=true, |
| | | $uidfetch=false, $return_uid=false) |
| | | /** |
| | | * Fetches specified header/data value for a set of messages. |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $message_set Searching criteria (list of messages to return) |
| | | * @param string $index_field Field to sort by (ARRIVAL, CC, DATE, FROM, SIZE, SUBJECT, TO) |
| | | * @param bool $skip_deleted Makes that DELETED messages will be skipped |
| | | * @param bool $uidfetch Enables UID FETCH usage |
| | | * @param bool $return_uid Enables returning UIDs instead of IDs |
| | | * |
| | | * @return array|bool List of header values or False on failure |
| | | */ |
| | | public function fetchHeaderIndex($mailbox, $message_set, $index_field = '', $skip_deleted = true, |
| | | $uidfetch = false, $return_uid = false) |
| | | { |
| | | if (is_array($message_set)) { |
| | | if (!($message_set = $this->compressMessageSet($message_set))) |
| | | if (!($message_set = $this->compressMessageSet($message_set))) { |
| | | return false; |
| | | } else { |
| | | } |
| | | } |
| | | else { |
| | | list($from_idx, $to_idx) = explode(':', $message_set); |
| | | if (empty($message_set) || |
| | | (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) { |
| | | (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx) |
| | | ) { |
| | | return false; |
| | | } |
| | | } |
| | |
| | | return false; |
| | | } |
| | | |
| | | /* Do "SELECT" command */ |
| | | // Select the mailbox |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | | } |
| | |
| | | $cmd = $uidfetch ? 'UID FETCH' : 'FETCH'; |
| | | $fields = array(); |
| | | |
| | | if ($return_uid) |
| | | if ($return_uid) { |
| | | $fields[] = 'UID'; |
| | | if ($skip_deleted) |
| | | } |
| | | if ($skip_deleted) { |
| | | $fields[] = 'FLAGS'; |
| | | } |
| | | |
| | | if ($mode == 1) { |
| | | if ($index_field == 'DATE') |
| | | if ($index_field == 'DATE') { |
| | | $fields[] = 'INTERNALDATE'; |
| | | } |
| | | $fields[] = "BODY.PEEK[HEADER.FIELDS ($index_field)]"; |
| | | } |
| | | else if ($mode == 2) { |
| | | if ($index_field == 'SIZE') |
| | | if ($index_field == 'SIZE') { |
| | | $fields[] = 'RFC822.SIZE'; |
| | | else if (!$return_uid || $index_field != 'UID') |
| | | } |
| | | else if (!$return_uid || $index_field != 'UID') { |
| | | $fields[] = $index_field; |
| | | } |
| | | } |
| | | else if ($mode == 3 && !$skip_deleted) |
| | | else if ($mode == 3 && !$skip_deleted) { |
| | | $fields[] = 'FLAGS'; |
| | | else if ($mode == 4) |
| | | } |
| | | else if ($mode == 4) { |
| | | $fields[] = 'INTERNALDATE'; |
| | | } |
| | | |
| | | $request = "$key $cmd $message_set (" . implode(' ', $fields) . ")"; |
| | | |
| | |
| | | $flags = null; |
| | | |
| | | if ($return_uid) { |
| | | if (preg_match('/UID ([0-9]+)/', $line, $matches)) |
| | | if (preg_match('/UID ([0-9]+)/', $line, $matches)) { |
| | | $id = (int) $matches[1]; |
| | | else |
| | | } |
| | | else { |
| | | continue; |
| | | } |
| | | } |
| | | if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { |
| | | $flags = explode(' ', strtoupper($matches[1])); |
| | |
| | | } |
| | | // non-existent/empty Date: header, use INTERNALDATE |
| | | if (empty($result[$id])) { |
| | | if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches)) |
| | | if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches)) { |
| | | $result[$id] = $this->strToTime($matches[1]); |
| | | else |
| | | } |
| | | else { |
| | | $result[$id] = 0; |
| | | } |
| | | } |
| | | } else if ($mode == 1) { |
| | | } |
| | | else if ($mode == 1) { |
| | | if (preg_match('/BODY\[HEADER\.FIELDS \("?(FROM|REPLY-TO|SENDER|TO|SUBJECT)"?\)\] (.*)/', $line, $matches)) { |
| | | $value = preg_replace(array('/^"*[a-z]+:/i', '/\s+$/sm'), array('', ''), $matches[2]); |
| | | $result[$id] = trim($value); |
| | | } else { |
| | | } |
| | | else { |
| | | $result[$id] = ''; |
| | | } |
| | | } else if ($mode == 2) { |
| | | } |
| | | else if ($mode == 2) { |
| | | if (preg_match('/' . $index_field . ' ([0-9]+)/', $line, $matches)) { |
| | | $result[$id] = trim($matches[1]); |
| | | } else { |
| | | } |
| | | else { |
| | | $result[$id] = 0; |
| | | } |
| | | } else if ($mode == 3) { |
| | | } |
| | | else if ($mode == 3) { |
| | | if (!$flags && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { |
| | | $flags = explode(' ', $matches[1]); |
| | | } |
| | | $result[$id] = in_array('\\'.$index_field, $flags) ? 1 : 0; |
| | | } else if ($mode == 4) { |
| | | $result[$id] = in_array("\\".$index_field, (array) $flags) ? 1 : 0; |
| | | } |
| | | else if ($mode == 4) { |
| | | if (preg_match('/INTERNALDATE "([^"]+)"/', $line, $matches)) { |
| | | $result[$id] = $this->strToTime($matches[1]); |
| | | } else { |
| | | } |
| | | else { |
| | | $result[$id] = 0; |
| | | } |
| | | } |
| | | } |
| | | } while (!$this->startsWith($line, $key, true, true)); |
| | | } |
| | | while (!$this->startsWith($line, $key, true, true)); |
| | | |
| | | return $result; |
| | | } |
| | |
| | | * |
| | | * @return int Message sequence identifier |
| | | */ |
| | | function UID2ID($mailbox, $uid) |
| | | public function UID2ID($mailbox, $uid) |
| | | { |
| | | if ($uid > 0) { |
| | | $index = $this->search($mailbox, "UID $uid"); |
| | |
| | | * |
| | | * @return int Message unique identifier |
| | | */ |
| | | function ID2UID($mailbox, $id) |
| | | public function ID2UID($mailbox, $id) |
| | | { |
| | | if (empty($id) || $id < 0) { |
| | | return null; |
| | |
| | | /** |
| | | * Sets flag of the message(s) |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | function flag($mailbox, $messages, $flag) { |
| | | public function flag($mailbox, $messages, $flag) |
| | | { |
| | | return $this->modFlag($mailbox, $messages, $flag, '+'); |
| | | } |
| | | |
| | | /** |
| | | * Unsets flag of the message(s) |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | function unflag($mailbox, $messages, $flag) { |
| | | public function unflag($mailbox, $messages, $flag) |
| | | { |
| | | return $this->modFlag($mailbox, $messages, $flag, '-'); |
| | | } |
| | | |
| | | /** |
| | | * Changes flag of the message(s) |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * @param string $mod Modifier [+|-]. Default: "+". |
| | | * @param string $mailbox Mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $flag Flag name |
| | | * @param string $mod Modifier [+|-]. Default: "+". |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | protected function modFlag($mailbox, $messages, $flag, $mod = '+') |
| | | { |
| | | if (!$flag) { |
| | | return false; |
| | | } |
| | | |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | | } |
| | |
| | | |
| | | if ($this->flags[strtoupper($flag)]) { |
| | | $flag = $this->flags[strtoupper($flag)]; |
| | | } |
| | | |
| | | if (!$flag) { |
| | | return false; |
| | | } |
| | | |
| | | // if PERMANENTFLAGS is not specified all flags are allowed |
| | |
| | | $this->compressMessageSet($messages), $mod . 'FLAGS.SILENT', "($flag)"), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | | * Copies message(s) from one folder to another |
| | | * |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $from Mailbox name |
| | | * @param string $to Destination mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $from Mailbox name |
| | | * @param string $to Destination mailbox name |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | function copy($messages, $from, $to) |
| | | public function copy($messages, $from, $to) |
| | | { |
| | | // Clear last COPYUID data |
| | | unset($this->data['COPYUID']); |
| | |
| | | $this->compressMessageSet($messages), $this->escape($to)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | /** |
| | | * Moves message(s) from one folder to another. |
| | | * |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $from Mailbox name |
| | | * @param string $to Destination mailbox name |
| | | * @param string|array $messages Message UID(s) |
| | | * @param string $from Mailbox name |
| | | * @param string $to Destination mailbox name |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | function move($messages, $from, $to) |
| | | public function move($messages, $from, $to) |
| | | { |
| | | if (!$this->select($from)) { |
| | | return false; |
| | |
| | | $this->compressMessageSet($messages), $this->escape($to)), |
| | | self::COMMAND_NORESPONSE); |
| | | |
| | | return ($result == self::ERROR_OK); |
| | | return $result == self::ERROR_OK; |
| | | } |
| | | |
| | | // use COPY + STORE +FLAGS.SILENT \Deleted + EXPUNGE |
| | |
| | | * @return array List of rcube_message_header elements, False on error |
| | | * @since 0.6 |
| | | */ |
| | | function fetch($mailbox, $message_set, $is_uid = false, $query_items = array(), |
| | | public function fetch($mailbox, $message_set, $is_uid = false, $query_items = array(), |
| | | $mod_seq = null, $vanished = false) |
| | | { |
| | | if (!$this->select($mailbox)) { |
| | |
| | | do { |
| | | $line = $this->readLine(4096); |
| | | |
| | | if (!$line) |
| | | if (!$line) { |
| | | break; |
| | | } |
| | | |
| | | // Sample reply line: |
| | | // * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen) |
| | |
| | | |
| | | while (strlen($out) < $bytes) { |
| | | $out = $this->readBytes($bytes); |
| | | if ($out === null) |
| | | if ($out === null) { |
| | | break; |
| | | } |
| | | $line .= $out; |
| | | } |
| | | |
| | | $str = $this->readLine(4096); |
| | | if ($str === false) |
| | | if ($str === false) { |
| | | break; |
| | | } |
| | | |
| | | $line .= $str; |
| | | } |
| | |
| | | else if ($name == 'FLAGS') { |
| | | if (!empty($value)) { |
| | | foreach ((array)$value as $flag) { |
| | | $flag = str_replace(array('$', '\\'), '', $flag); |
| | | $flag = str_replace(array('$', "\\"), '', $flag); |
| | | $flag = strtoupper($flag); |
| | | |
| | | $result[$id]->flags[$flag] = true; |
| | |
| | | $this->tokenizeResponse($line, 1); |
| | | $headers = $this->tokenizeResponse($line, 1); |
| | | } |
| | | else if (strlen($name)) |
| | | else if (strlen($name)) { |
| | | $result[$id]->bodypart[$name] = $value; |
| | | else |
| | | } |
| | | else { |
| | | $result[$id]->body = $value; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | foreach ($headers as $resln) { |
| | | if (ord($resln[0]) <= 32) { |
| | | $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln); |
| | | } else { |
| | | } |
| | | else { |
| | | $lines[++$ln] = trim($resln); |
| | | } |
| | | } |
| | |
| | | $result[$id]->date = $string; |
| | | $result[$id]->timestamp = $this->strToTime($string); |
| | | break; |
| | | case 'from': |
| | | $result[$id]->from = $string; |
| | | break; |
| | | case 'to': |
| | | $result[$id]->to = preg_replace('/undisclosed-recipients:[;,]*/', '', $string); |
| | | break; |
| | | case 'from': |
| | | case 'subject': |
| | | $result[$id]->subject = $string; |
| | | case 'cc': |
| | | case 'bcc': |
| | | case 'references': |
| | | $result[$id]->{$field} = $string; |
| | | break; |
| | | case 'reply-to': |
| | | $result[$id]->replyto = $string; |
| | | break; |
| | | case 'cc': |
| | | $result[$id]->cc = $string; |
| | | break; |
| | | case 'bcc': |
| | | $result[$id]->bcc = $string; |
| | | break; |
| | | case 'content-transfer-encoding': |
| | | $result[$id]->encoding = $string; |
| | |
| | | break; |
| | | case 'in-reply-to': |
| | | $result[$id]->in_reply_to = str_replace(array("\n", '<', '>'), '', $string); |
| | | break; |
| | | case 'references': |
| | | $result[$id]->references = $string; |
| | | break; |
| | | case 'return-receipt-to': |
| | | case 'disposition-notification-to': |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // VANISHED response (QRESYNC RFC5162) |
| | | // Sample: * VANISHED (EARLIER) 300:310,405,411 |
| | | else if (preg_match('/^\* VANISHED [()EARLIER]*/i', $line, $match)) { |
| | |
| | | |
| | | $this->data['VANISHED'] = $v_data; |
| | | } |
| | | |
| | | } while (!$this->startsWith($line, $key, true)); |
| | | } |
| | | while (!$this->startsWith($line, $key, true)); |
| | | |
| | | return $result; |
| | | } |
| | |
| | | * |
| | | * @return bool|array List of rcube_message_header elements, False on error |
| | | */ |
| | | function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add_headers = array()) |
| | | public function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add_headers = array()) |
| | | { |
| | | $query_items = array('UID', 'RFC822.SIZE', 'FLAGS', 'INTERNALDATE'); |
| | | $headers = array('DATE', 'FROM', 'TO', 'SUBJECT', 'CONTENT-TYPE', 'CC', 'REPLY-TO', |
| | |
| | | |
| | | $query_items[] = 'BODY.PEEK[HEADER.FIELDS (' . implode(' ', $headers) . ')]'; |
| | | |
| | | $result = $this->fetch($mailbox, $message_set, $is_uid, $query_items); |
| | | |
| | | return $result; |
| | | return $this->fetch($mailbox, $message_set, $is_uid, $query_items); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @return bool|rcube_message_header Message data, False on error |
| | | */ |
| | | function fetchHeader($mailbox, $id, $is_uid = false, $bodystr = false, $add_headers = array()) |
| | | public function fetchHeader($mailbox, $id, $is_uid = false, $bodystr = false, $add_headers = array()) |
| | | { |
| | | $a = $this->fetchHeaders($mailbox, $id, $is_uid, $bodystr, $add_headers); |
| | | if (is_array($a)) { |
| | | return array_shift($a); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | |
| | | */ |
| | | public static function sortHeaders($messages, $field, $flag) |
| | | { |
| | | if (empty($field)) { |
| | | $field = 'uid'; |
| | | } |
| | | else { |
| | | $field = strtolower($field); |
| | | } |
| | | |
| | | if (empty($flag)) { |
| | | $flag = 'ASC'; |
| | | } |
| | | else { |
| | | $flag = strtoupper($flag); |
| | | } |
| | | |
| | | // Strategy: First, we'll create an "index" array. |
| | | // Then, we'll use sort() on that array, and use that to sort the main array. |
| | | |
| | | $field = empty($field) ? 'uid' : strtolower($field); |
| | | $flag = empty($flag) ? 'ASC' : strtoupper($flag); |
| | | $index = array(); |
| | | $result = array(); |
| | | |
| | |
| | | return $result; |
| | | } |
| | | |
| | | function fetchMIMEHeaders($mailbox, $uid, $parts, $mime=true) |
| | | /** |
| | | * Fetch MIME headers of specified message parts |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * @param int $uid Message UID |
| | | * @param array $parts Message part identifiers |
| | | * @param bool $mime Use MIME instad of HEADER |
| | | * |
| | | * @return array|bool Array containing headers string for each specified body |
| | | * False on failure. |
| | | */ |
| | | public function fetchMIMEHeaders($mailbox, $uid, $parts, $mime = true) |
| | | { |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | |
| | | do { |
| | | $line = $this->readLine(1024); |
| | | |
| | | if (preg_match('/^\* [0-9]+ FETCH [0-9UID( ]+BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) { |
| | | $idx = $matches[1]; |
| | | $headers = ''; |
| | | |
| | | // get complete entry |
| | | if (preg_match('/\{([0-9]+)\}\r\n$/', $line, $m)) { |
| | | $bytes = $m[1]; |
| | | $out = ''; |
| | | |
| | | while (strlen($out) < $bytes) { |
| | | $out = $this->readBytes($bytes); |
| | | if ($out === null) |
| | | break; |
| | | $headers .= $out; |
| | | } |
| | | if (preg_match('/^\* [0-9]+ FETCH [0-9UID( ]+/', $line, $m)) { |
| | | $line = ltrim(substr($line, strlen($m[0]))); |
| | | while (preg_match('/^BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) { |
| | | $line = substr($line, strlen($matches[0])); |
| | | $result[$matches[1]] = trim($this->multLine($line)); |
| | | $line = ltrim($this->readLine(1024)); |
| | | } |
| | | |
| | | $result[$idx] = trim($headers); |
| | | } |
| | | } while (!$this->startsWith($line, $key, true)); |
| | | } |
| | | while (!$this->startsWith($line, $key, true)); |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | function fetchPartHeader($mailbox, $id, $is_uid = false, $part = null) |
| | | /** |
| | | * Fetches message part header |
| | | */ |
| | | public function fetchPartHeader($mailbox, $id, $is_uid = false, $part = null) |
| | | { |
| | | $part = empty($part) ? 'HEADER' : $part.'.MIME'; |
| | | |
| | | return $this->handlePartBody($mailbox, $id, $is_uid, $part); |
| | | } |
| | | |
| | | function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=null, $print=null, $file=null, $formatted=false, $max_bytes=0) |
| | | /** |
| | | * Fetches body of the specified message part |
| | | */ |
| | | public function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=null, $print=null, $file=null, $formatted=false, $max_bytes=0) |
| | | { |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | | } |
| | | |
| | | $binary = true; |
| | | $binary = true; |
| | | |
| | | do { |
| | | if (!$initiated) { |
| | |
| | | } |
| | | } |
| | | } |
| | | } while (!$this->startsWith($line, $key, true) || !$initiated); |
| | | } |
| | | while (!$this->startsWith($line, $key, true) || !$initiated); |
| | | |
| | | if ($result !== false) { |
| | | if ($file) { |
| | |
| | | * |
| | | * @return string|bool On success APPENDUID response (if available) or True, False on failure |
| | | */ |
| | | function append($mailbox, &$message, $flags = array(), $date = null, $binary = false) |
| | | public function append($mailbox, &$message, $flags = array(), $date = null, $binary = false) |
| | | { |
| | | unset($this->data['APPENDUID']); |
| | | |
| | |
| | | * |
| | | * @return string|bool On success APPENDUID response (if available) or True, False on failure |
| | | */ |
| | | function appendFromFile($mailbox, $path, $headers=null, $flags = array(), $date = null, $binary = false) |
| | | public function appendFromFile($mailbox, $path, $headers=null, $flags = array(), $date = null, $binary = false) |
| | | { |
| | | // open message file |
| | | if (file_exists(realpath($path))) { |
| | |
| | | * |
| | | * @return array Quota information |
| | | */ |
| | | function getQuota($mailbox = null) |
| | | public function getQuota($mailbox = null) |
| | | { |
| | | if ($mailbox === null || $mailbox === '') { |
| | | $mailbox = 'INBOX'; |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function setACL($mailbox, $user, $acl) |
| | | public function setACL($mailbox, $user, $acl) |
| | | { |
| | | if (is_array($acl)) { |
| | | $acl = implode('', $acl); |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function deleteACL($mailbox, $user) |
| | | public function deleteACL($mailbox, $user) |
| | | { |
| | | $result = $this->execute('DELETEACL', array( |
| | | $this->escape($mailbox), $this->escape($user)), |
| | |
| | | * @return array User-rights array on success, NULL on error |
| | | * @since 0.5-beta |
| | | */ |
| | | function getACL($mailbox) |
| | | public function getACL($mailbox) |
| | | { |
| | | list($code, $response) = $this->execute('GETACL', array($this->escape($mailbox))); |
| | | |
| | |
| | | * @return array List of user rights |
| | | * @since 0.5-beta |
| | | */ |
| | | function listRights($mailbox, $user) |
| | | public function listRights($mailbox, $user) |
| | | { |
| | | list($code, $response) = $this->execute('LISTRIGHTS', array( |
| | | $this->escape($mailbox), $this->escape($user))); |
| | |
| | | * @return array MYRIGHTS response on success, NULL on error |
| | | * @since 0.5-beta |
| | | */ |
| | | function myRights($mailbox) |
| | | public function myRights($mailbox) |
| | | { |
| | | list($code, $response) = $this->execute('MYRIGHTS', array($this->escape($mailbox))); |
| | | |
| | |
| | | * @return boolean True on success, False on failure |
| | | * @since 0.5-beta |
| | | */ |
| | | function setMetadata($mailbox, $entries) |
| | | public function setMetadata($mailbox, $entries) |
| | | { |
| | | if (!is_array($entries) || empty($entries)) { |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command"); |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function deleteMetadata($mailbox, $entries) |
| | | public function deleteMetadata($mailbox, $entries) |
| | | { |
| | | if (!is_array($entries) && !empty($entries)) { |
| | | $entries = explode(' ', $entries); |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function getMetadata($mailbox, $entries, $options=array()) |
| | | public function getMetadata($mailbox, $entries, $options=array()) |
| | | { |
| | | if (!is_array($entries)) { |
| | | $entries = array($entries); |
| | |
| | | * @return boolean True on success, False on failure |
| | | * @since 0.5-beta |
| | | */ |
| | | function setAnnotation($mailbox, $data) |
| | | public function setAnnotation($mailbox, $data) |
| | | { |
| | | if (!is_array($data) || empty($data)) { |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function deleteAnnotation($mailbox, $data) |
| | | public function deleteAnnotation($mailbox, $data) |
| | | { |
| | | if (!is_array($data) || empty($data)) { |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | |
| | | * |
| | | * @since 0.5-beta |
| | | */ |
| | | function getAnnotation($mailbox, $entries, $attribs) |
| | | public function getAnnotation($mailbox, $entries, $attribs) |
| | | { |
| | | if (!is_array($entries)) { |
| | | $entries = array($entries); |
| | |
| | | * @return array/bool Body structure array or False on error. |
| | | * @since 0.6 |
| | | */ |
| | | function getStructure($mailbox, $id, $is_uid = false) |
| | | public function getStructure($mailbox, $id, $is_uid = false) |
| | | { |
| | | $result = $this->fetch($mailbox, $id, $is_uid, array('BODYSTRUCTURE')); |
| | | |
| | | if (is_array($result)) { |
| | | $result = array_shift($result); |
| | | return $result->bodystructure; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | |
| | | * |
| | | * @return array Part data as hash array (type, encoding, charset, size) |
| | | */ |
| | | static function getStructurePartData($structure, $part) |
| | | public static function getStructurePartData($structure, $part) |
| | | { |
| | | $part_a = self::getStructurePartArray($structure, $part); |
| | | $data = array(); |
| | |
| | | return $data; |
| | | } |
| | | |
| | | static function getStructurePartArray($a, $part) |
| | | public static function getStructurePartArray($a, $part) |
| | | { |
| | | if (!is_array($a)) { |
| | | return false; |
| | |
| | | * @return string Command identifier |
| | | * @since 0.5-beta |
| | | */ |
| | | function nextTag() |
| | | public function nextTag() |
| | | { |
| | | $this->cmd_num++; |
| | | $this->cmd_tag = sprintf('A%04d', $this->cmd_num); |
| | |
| | | * @return mixed Response code or list of response code and data |
| | | * @since 0.5-beta |
| | | */ |
| | | function execute($command, $arguments=array(), $options=0) |
| | | public function execute($command, $arguments=array(), $options=0) |
| | | { |
| | | $tag = $this->nextTag(); |
| | | $query = $tag . ' ' . $command; |
| | |
| | | if ($response !== null) { |
| | | $response .= $line; |
| | | } |
| | | } while (!$this->startsWith($line, $tag . ' ', true, true)); |
| | | } |
| | | while (!$this->startsWith($line, $tag . ' ', true, true)); |
| | | |
| | | $code = $this->parseResult($line, $command . ': '); |
| | | |
| | |
| | | * @return mixed Tokens array or string if $num=1 |
| | | * @since 0.5-beta |
| | | */ |
| | | static function tokenizeResponse(&$str, $num=0) |
| | | public static function tokenizeResponse(&$str, $num=0) |
| | | { |
| | | $result = array(); |
| | | |
| | |
| | | return $num == 1 ? $result[0] : $result; |
| | | } |
| | | |
| | | static function r_implode($element) |
| | | protected static function r_implode($element) |
| | | { |
| | | $string = ''; |
| | | |
| | |
| | | * |
| | | * @return string Compressed sequence-set |
| | | */ |
| | | static function compressMessageSet($messages, $force=false) |
| | | public static function compressMessageSet($messages, $force=false) |
| | | { |
| | | // given a comma delimited list of independent mid's, |
| | | // compresses by grouping sequences together |
| | |
| | | if ($incr > 1) { // found a gap |
| | | if ($start == $prev) { |
| | | $result[] = $prev; // push single id |
| | | } else { |
| | | } |
| | | else { |
| | | $result[] = $start . ':' . $prev; // push sequence as start_id:end_id |
| | | } |
| | | $start = $id; // start of new sequence |
| | |
| | | // handle the last sequence/id |
| | | if ($start == $prev) { |
| | | $result[] = $prev; |
| | | } else { |
| | | } |
| | | else { |
| | | $result[] = $start.':'.$prev; |
| | | } |
| | | |
| | |
| | | * |
| | | * @return array List of message identifiers |
| | | */ |
| | | static function uncompressMessageSet($messages) |
| | | public static function uncompressMessageSet($messages) |
| | | { |
| | | if (empty($messages)) { |
| | | return array(); |
| | |
| | | * |
| | | * @return int Unix timestamp |
| | | */ |
| | | static function strToTime($date) |
| | | protected static function strToTime($date) |
| | | { |
| | | // Clean malformed data |
| | | $date = preg_replace( |
| | |
| | | * @return string String atom, quoted-string or string literal |
| | | * @todo lists |
| | | */ |
| | | static function escape($string, $force_quotes=false) |
| | | public static function escape($string, $force_quotes=false) |
| | | { |
| | | if ($string === null) { |
| | | return 'NIL'; |
| | |
| | | * |
| | | * @since 0.5-stable |
| | | */ |
| | | function setDebug($debug, $handler = null) |
| | | public function setDebug($debug, $handler = null) |
| | | { |
| | | $this->debug = $debug; |
| | | $this->debug = $debug; |
| | | $this->debug_handler = $handler; |
| | | } |
| | | |
| | |
| | | |
| | | if ($this->debug_handler) { |
| | | call_user_func_array($this->debug_handler, array(&$this, $message)); |
| | | } else { |
| | | } |
| | | else { |
| | | echo "DEBUG: $message\n"; |
| | | } |
| | | } |