| | |
| | | return self::ERROR_UNKNOWN; |
| | | } |
| | | |
| | | private function set_error($code, $msg='') |
| | | private function setError($code, $msg='') |
| | | { |
| | | $this->errornum = $code; |
| | | $this->error = $msg; |
| | |
| | | { |
| | | if ($type == 'CRAM-MD5' || $type == 'DIGEST-MD5') { |
| | | if ($type == 'DIGEST-MD5' && !class_exists('Auth_SASL')) { |
| | | $this->set_error(self::ERROR_BYE, |
| | | $this->setError(self::ERROR_BYE, |
| | | "The Auth_SASL package is required for DIGEST-MD5 authentication"); |
| | | return self::ERROR_BAD; |
| | | } |
| | | |
| | | $this->putLine($this->next_tag() . " AUTHENTICATE $type"); |
| | | $this->putLine($this->nextTag() . " AUTHENTICATE $type"); |
| | | $line = trim($this->readLine(1024)); |
| | | |
| | | if ($line[0] == '+') { |
| | |
| | | // check response |
| | | $challenge = base64_decode($challenge); |
| | | if (strpos($challenge, 'rspauth=') === false) { |
| | | $this->set_error(self::ERROR_BAD, |
| | | $this->setError(self::ERROR_BAD, |
| | | "Unexpected response from server to DIGEST-MD5 response"); |
| | | return self::ERROR_BAD; |
| | | } |
| | |
| | | self::COMMAND_NORESPONSE | self::COMMAND_CAPABILITY); |
| | | } |
| | | else { |
| | | $this->putLine($this->next_tag() . " AUTHENTICATE PLAIN"); |
| | | $this->putLine($this->nextTag() . " AUTHENTICATE PLAIN"); |
| | | $line = trim($this->readLine(1024)); |
| | | |
| | | if ($line[0] != '+') { |
| | |
| | | return $this->fp; |
| | | } |
| | | else { |
| | | $this->set_error($result, "Unable to authenticate user ($type): $line"); |
| | | $this->setError($result, "Unable to authenticate user ($type): $line"); |
| | | } |
| | | |
| | | return $result; |
| | |
| | | |
| | | // check input |
| | | if (empty($host)) { |
| | | $this->set_error(self::ERROR_BAD, "Empty host"); |
| | | $this->setError(self::ERROR_BAD, "Empty host"); |
| | | return false; |
| | | } |
| | | if (empty($user)) { |
| | | $this->set_error(self::ERROR_NO, "Empty user"); |
| | | $this->setError(self::ERROR_NO, "Empty user"); |
| | | return false; |
| | | } |
| | | if (empty($password)) { |
| | | $this->set_error(self::ERROR_NO, "Empty password"); |
| | | $this->setError(self::ERROR_NO, "Empty password"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr); |
| | | |
| | | if (!$this->fp) { |
| | | $this->set_error(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr)); |
| | | $this->setError(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr)); |
| | | return false; |
| | | } |
| | | |
| | |
| | | else |
| | | $error = sprintf("Empty startup greeting (%s:%d)", $host, $this->prefs['port']); |
| | | |
| | | $this->set_error(self::ERROR_BAD, $error); |
| | | $this->setError(self::ERROR_BAD, $error); |
| | | $this->close(); |
| | | return false; |
| | | } |
| | |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | $this->set_error(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | $this->setError(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | $this->close(); |
| | | return false; |
| | | } |
| | |
| | | else { |
| | | // Prevent from sending credentials in plain text when connection is not secure |
| | | if ($auth_method == 'LOGIN' && $this->getCapability('LOGINDISABLED')) { |
| | | $this->set_error(self::ERROR_BAD, "Login disabled by IMAP server"); |
| | | $this->setError(self::ERROR_BAD, "Login disabled by IMAP server"); |
| | | $this->close(); |
| | | return false; |
| | | } |
| | |
| | | $result = $this->login($user, $password); |
| | | break; |
| | | default: |
| | | $this->set_error(self::ERROR_BAD, "Configuration error. Unknown auth method: $method"); |
| | | $this->setError(self::ERROR_BAD, "Configuration error. Unknown auth method: $method"); |
| | | } |
| | | |
| | | if (is_resource($result)) { |
| | |
| | | |
| | | function close() |
| | | { |
| | | if ($this->putLine($this->next_tag() . ' LOGOUT')) { |
| | | if ($this->putLine($this->nextTag() . ' LOGOUT')) { |
| | | $this->readReply(); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // build FETCH command string |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $cmd = $uidfetch ? 'UID FETCH' : 'FETCH'; |
| | | $deleted = $skip_deleted ? ' FLAGS' : ''; |
| | | |
| | |
| | | $request = $key . $request; |
| | | |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $add = ' '.trim($add); |
| | | |
| | | /* FETCH uid, size, flags and headers */ |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($uidfetch ? ' UID' : '') . " FETCH $message_set "; |
| | | $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE "; |
| | | if ($bodystr) |
| | |
| | | $request .= "LIST-POST DISPOSITION-NOTIFICATION-TO".$add.")])"; |
| | | |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | do { |
| | |
| | | if ($r) { |
| | | // Clear internal status cache |
| | | unset($this->data['STATUS:'.$from]); |
| | | |
| | | |
| | | return $this->delete($from, $messages); |
| | | } |
| | | return $r; |
| | |
| | | $response = str_replace("\r\n", '', $response); |
| | | |
| | | if ($esearch) { |
| | | // Skip prefix: ... (TAG "A285") UID ... |
| | | // Skip prefix: ... (TAG "A285") UID ... |
| | | $this->tokenizeResponse($response, $return_uid ? 2 : 1); |
| | | |
| | | $result = array(); |
| | |
| | | if (in_array('ALL', $items)) |
| | | $result['ALL'] = $this->compressMessageSet($response, true); |
| | | |
| | | return $result; |
| | | return $result; |
| | | } |
| | | else { |
| | | return $response; |
| | |
| | | |
| | | $result = false; |
| | | $parts = (array) $parts; |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $peeks = ''; |
| | | $idx = 0; |
| | | $type = $mime ? 'MIME' : 'HEADER'; |
| | |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | // format request |
| | | $reply_key = '* ' . $id; |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id (BODY.PEEK[$part])"; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | return false; |
| | | } |
| | | |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $request = sprintf("$key APPEND %s (\\Seen) {%d%s}", $this->escape($mailbox), |
| | | $len, ($this->prefs['literal+'] ? '+' : '')); |
| | | |
| | |
| | | return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | } |
| | | |
| | | return false; |
| | |
| | | $in_fp = fopen($path, 'r'); |
| | | } |
| | | if (!$in_fp) { |
| | | $this->set_error(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); |
| | | $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // send APPEND command |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $request = sprintf("$key APPEND %s (\\Seen) {%d%s}", $this->escape($mailbox), |
| | | $len, ($this->prefs['literal+'] ? '+' : '')); |
| | | |
| | |
| | | return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | } |
| | | |
| | | return false; |
| | |
| | | return false; |
| | | } |
| | | |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $result = false; |
| | | $command = $key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)"; |
| | | |
| | |
| | | $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -1)); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | } |
| | | |
| | | return $result; |
| | |
| | | */ |
| | | $result = false; |
| | | $quota_lines = array(); |
| | | $key = $this->next_tag(); |
| | | $key = $this->nextTag(); |
| | | $command = $key . ' GETQUOTAROOT INBOX'; |
| | | |
| | | // get line(s) containing quota info |
| | |
| | | } while (!$this->startsWith($line, $key, true, true)); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | } |
| | | |
| | | // return false if not found, parse if found |
| | |
| | | return $ret; |
| | | } |
| | | |
| | | $this->set_error(self::ERROR_COMMAND, "Incomplete ACL response"); |
| | | $this->setError(self::ERROR_COMMAND, "Incomplete ACL response"); |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | function setMetadata($mailbox, $entries) |
| | | { |
| | | if (!is_array($entries) || empty($entries)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command"); |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $entries = explode(' ', $entries); |
| | | |
| | | if (empty($entries)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command"); |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | function setAnnotation($mailbox, $data) |
| | | { |
| | | if (!is_array($data) || empty($data)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | function deleteAnnotation($mailbox, $data) |
| | | { |
| | | if (!is_array($data) || empty($data)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | | $this->setError(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | * @access public |
| | | * @since 0.5-beta |
| | | */ |
| | | function next_tag() |
| | | function nextTag() |
| | | { |
| | | $this->cmd_num++; |
| | | $this->cmd_tag = sprintf('A%04d', $this->cmd_num); |
| | |
| | | */ |
| | | function execute($command, $arguments=array(), $options=0) |
| | | { |
| | | $tag = $this->next_tag(); |
| | | $tag = $this->nextTag(); |
| | | $query = $tag . ' ' . $command; |
| | | $noresp = ($options & self::COMMAND_NORESPONSE); |
| | | $response = $noresp ? null : ''; |
| | |
| | | |
| | | // Send command |
| | | if (!$this->putLineC($query)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $query"); |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $query"); |
| | | return $noresp ? self::ERROR_COMMAND : array(self::ERROR_COMMAND, ''); |
| | | } |
| | | |
| | |
| | | return $ts < 0 ? 0 : $ts; |
| | | } |
| | | |
| | | private function SplitHeaderLine($string) |
| | | private function splitHeaderLine($string) |
| | | { |
| | | $pos = strpos($string, ':'); |
| | | if ($pos>0) { |