| | |
| | | function login($user, $password) |
| | | { |
| | | list($code, $response) = $this->execute('LOGIN', array( |
| | | $this->escape($user), $this->escape($password)), self::COMMAND_CAPABILITY); |
| | | $this->escape($user), $this->escape($password)), self::COMMAND_CAPABILITY | self::COMMAND_ANONYMIZED); |
| | | |
| | | // re-set capabilities list if untagged CAPABILITY response provided |
| | | if (preg_match('/\* CAPABILITY (.+)/i', $response, $matches)) { |
| | |
| | | // configure |
| | | $this->set_prefs($options); |
| | | |
| | | $auth_method = $this->prefs['auth_type']; |
| | | $result = false; |
| | | |
| | | // initialize connection |
| | | $this->error = ''; |
| | | $this->errornum = self::ERROR_OK; |
| | | $this->selected = null; |
| | | $this->user = $user; |
| | | $this->host = $host; |
| | | $this->user = $user; |
| | | $this->logged = false; |
| | | $this->selected = null; |
| | | |
| | | // check input |
| | | if (empty($host)) { |
| | | $this->setError(self::ERROR_BAD, "Empty host"); |
| | | return false; |
| | | } |
| | | |
| | | if (empty($user)) { |
| | | $this->setError(self::ERROR_NO, "Empty user"); |
| | | return false; |
| | | } |
| | | |
| | | if (empty($password)) { |
| | | $this->setError(self::ERROR_NO, "Empty password"); |
| | | return false; |
| | | } |
| | | |
| | | if (!$this->prefs['port']) { |
| | | $this->prefs['port'] = 143; |
| | | } |
| | | // check for SSL |
| | | if ($this->prefs['ssl_mode'] && $this->prefs['ssl_mode'] != 'tls') { |
| | | $host = $this->prefs['ssl_mode'] . '://' . $host; |
| | | } |
| | | |
| | | if ($this->prefs['timeout'] <= 0) { |
| | | $this->prefs['timeout'] = max(0, intval(ini_get('default_socket_timeout'))); |
| | | } |
| | | |
| | | // Connect |
| | | $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr, $this->prefs['timeout']); |
| | | |
| | | if (!$this->fp) { |
| | | if (!$errstr) { |
| | | $errstr = "Unknown reason (fsockopen() function disabled?)"; |
| | | } |
| | | $this->setError(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr)); |
| | | if (!$this->_connect($host)) { |
| | | return false; |
| | | } |
| | | |
| | | if ($this->prefs['timeout'] > 0) { |
| | | stream_set_timeout($this->fp, $this->prefs['timeout']); |
| | | } |
| | | |
| | | $line = trim(fgets($this->fp, 8192)); |
| | | |
| | | if ($this->_debug) { |
| | | // set connection identifier for debug output |
| | | preg_match('/#([0-9]+)/', (string)$this->fp, $m); |
| | | $this->resourceid = strtoupper(substr(md5($m[1].$this->user.microtime()), 0, 4)); |
| | | |
| | | if ($line) |
| | | $this->debug('S: '. $line); |
| | | } |
| | | |
| | | // Connected to wrong port or connection error? |
| | | if (!preg_match('/^\* (OK|PREAUTH)/i', $line)) { |
| | | if ($line) |
| | | $error = sprintf("Wrong startup greeting (%s:%d): %s", $host, $this->prefs['port'], $line); |
| | | else |
| | | $error = sprintf("Empty startup greeting (%s:%d)", $host, $this->prefs['port']); |
| | | |
| | | $this->setError(self::ERROR_BAD, $error); |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | // RFC3501 [7.1] optional CAPABILITY response |
| | | if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) { |
| | | $this->parseCapability($matches[1], true); |
| | | } |
| | | |
| | | // TLS connection |
| | | if ($this->prefs['ssl_mode'] == 'tls' && $this->getCapability('STARTTLS')) { |
| | | $res = $this->execute('STARTTLS'); |
| | | |
| | | if ($res[0] != self::ERROR_OK) { |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | $this->setError(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | // Now we're secure, capabilities need to be reread |
| | | $this->clearCapability(); |
| | | } |
| | | |
| | | // Send ID info |
| | |
| | | $this->id($this->prefs['ident']); |
| | | } |
| | | |
| | | $auth_method = $this->prefs['auth_type']; |
| | | $auth_methods = array(); |
| | | $result = null; |
| | | |
| | |
| | | $this->closeConnection(); |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Connects to IMAP server. |
| | | * |
| | | * @param string $host Server hostname or IP |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | | protected function _connect($host) |
| | | { |
| | | // initialize connection |
| | | $this->error = ''; |
| | | $this->errornum = self::ERROR_OK; |
| | | |
| | | if (!$this->prefs['port']) { |
| | | $this->prefs['port'] = 143; |
| | | } |
| | | |
| | | // check for SSL |
| | | if ($this->prefs['ssl_mode'] && $this->prefs['ssl_mode'] != 'tls') { |
| | | $host = $this->prefs['ssl_mode'] . '://' . $host; |
| | | } |
| | | |
| | | if ($this->prefs['timeout'] <= 0) { |
| | | $this->prefs['timeout'] = max(0, intval(ini_get('default_socket_timeout'))); |
| | | } |
| | | |
| | | if (!empty($this->prefs['socket_options'])) { |
| | | $context = stream_context_create($this->prefs['socket_options']); |
| | | $this->fp = stream_socket_client($host . ':' . $this->prefs['port'], $errno, $errstr, |
| | | $this->prefs['timeout'], STREAM_CLIENT_CONNECT, $context); |
| | | } |
| | | else { |
| | | $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr, $this->prefs['timeout']); |
| | | } |
| | | |
| | | if (!$this->fp) { |
| | | $this->setError(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", |
| | | $host, $this->prefs['port'], $errstr ?: "Unknown reason")); |
| | | |
| | | return false; |
| | | } |
| | | |
| | | if ($this->prefs['timeout'] > 0) { |
| | | stream_set_timeout($this->fp, $this->prefs['timeout']); |
| | | } |
| | | |
| | | $line = trim(fgets($this->fp, 8192)); |
| | | |
| | | if ($this->_debug) { |
| | | // set connection identifier for debug output |
| | | preg_match('/#([0-9]+)/', (string) $this->fp, $m); |
| | | $this->resourceid = strtoupper(substr(md5($m[1].$this->user.microtime()), 0, 4)); |
| | | |
| | | if ($line) { |
| | | $this->debug('S: '. $line); |
| | | } |
| | | } |
| | | |
| | | // Connected to wrong port or connection error? |
| | | if (!preg_match('/^\* (OK|PREAUTH)/i', $line)) { |
| | | if ($line) |
| | | $error = sprintf("Wrong startup greeting (%s:%d): %s", $host, $this->prefs['port'], $line); |
| | | else |
| | | $error = sprintf("Empty startup greeting (%s:%d)", $host, $this->prefs['port']); |
| | | |
| | | $this->setError(self::ERROR_BAD, $error); |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | // RFC3501 [7.1] optional CAPABILITY response |
| | | if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) { |
| | | $this->parseCapability($matches[1], true); |
| | | } |
| | | |
| | | // TLS connection |
| | | if ($this->prefs['ssl_mode'] == 'tls' && $this->getCapability('STARTTLS')) { |
| | | $res = $this->execute('STARTTLS'); |
| | | |
| | | if ($res[0] != self::ERROR_OK) { |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | $this->setError(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | $this->closeConnection(); |
| | | return false; |
| | | } |
| | | |
| | | // Now we're secure, capabilities need to be reread |
| | | $this->clearCapability(); |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | |
| | | $result[$id] = ''; |
| | | } |
| | | } else if ($mode == 2) { |
| | | if (preg_match('/(UID|RFC822\.SIZE) ([0-9]+)/', $line, $matches)) { |
| | | $result[$id] = trim($matches[2]); |
| | | if (preg_match('/' . $index_field . ' ([0-9]+)/', $line, $matches)) { |
| | | $result[$id] = trim($matches[1]); |
| | | } else { |
| | | $result[$id] = 0; |
| | | } |
| | |
| | | */ |
| | | private function modFlag($mailbox, $messages, $flag, $mod = '+') |
| | | { |
| | | if ($mod != '+' && $mod != '-') { |
| | | $mod = '+'; |
| | | } |
| | | |
| | | if (!$this->select($mailbox)) { |
| | | return false; |
| | | } |
| | |
| | | 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 |
| | | if (!empty($this->data['PERMANENTFLAGS']) |
| | | && !in_array($flag, (array) $this->data['PERMANENTFLAGS']) |
| | | && !in_array('\\*', (array) $this->data['PERMANENTFLAGS']) |
| | | ) { |
| | | return false; |
| | | } |
| | | |
| | | // Clear internal status cache |
| | | if ($flag == 'SEEN') { |
| | | unset($this->data['STATUS:'.$mailbox]['UNSEEN']); |
| | | } |
| | | |
| | | $flag = $this->flags[strtoupper($flag)]; |
| | | if ($mod != '+' && $mod != '-') { |
| | | $mod = '+'; |
| | | } |
| | | |
| | | $result = $this->execute('UID STORE', array( |
| | | $this->compressMessageSet($messages), $mod . 'FLAGS.SILENT', "($flag)"), |
| | | self::COMMAND_NORESPONSE); |
| | |
| | | return false; |
| | | } |
| | | |
| | | switch ($encoding) { |
| | | case 'base64': |
| | | $mode = 1; |
| | | break; |
| | | case 'quoted-printable': |
| | | $mode = 2; |
| | | break; |
| | | case 'x-uuencode': |
| | | case 'x-uue': |
| | | case 'uue': |
| | | case 'uuencode': |
| | | $mode = 3; |
| | | break; |
| | | default: |
| | | $mode = 0; |
| | | } |
| | | |
| | | // Use BINARY extension when possible (and safe) |
| | | $binary = $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY'); |
| | | $fetch_mode = $binary ? 'BINARY' : 'BODY'; |
| | | $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : ''; |
| | | |
| | | // format request |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)"; |
| | | $result = false; |
| | | $found = false; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | | if ($binary) { |
| | | // WARNING: Use $formatted argument with care, this may break binary data stream |
| | | $mode = -1; |
| | | } |
| | | $initiated = false; |
| | | $binary = true; |
| | | |
| | | do { |
| | | if (!$initiated) { |
| | | switch ($encoding) { |
| | | case 'base64': |
| | | $mode = 1; |
| | | break; |
| | | case 'quoted-printable': |
| | | $mode = 2; |
| | | break; |
| | | case 'x-uuencode': |
| | | case 'x-uue': |
| | | case 'uue': |
| | | case 'uuencode': |
| | | $mode = 3; |
| | | break; |
| | | default: |
| | | $mode = 0; |
| | | } |
| | | |
| | | // Use BINARY extension when possible (and safe) |
| | | $binary = $binary && $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY'); |
| | | $fetch_mode = $binary ? 'BINARY' : 'BODY'; |
| | | $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : ''; |
| | | |
| | | // format request |
| | | $key = $this->nextTag(); |
| | | $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)"; |
| | | $result = false; |
| | | $found = false; |
| | | $initiated = true; |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | | if ($binary) { |
| | | // WARNING: Use $formatted argument with care, this may break binary data stream |
| | | $mode = -1; |
| | | } |
| | | } |
| | | |
| | | $line = trim($this->readLine(1024)); |
| | | |
| | | if (!$line) { |
| | | break; |
| | | } |
| | | |
| | | // handle UNKNOWN-CTE response - RFC 3516, try again with standard BODY request |
| | | if ($binary && !$found && preg_match('/^' . $key . ' NO \[UNKNOWN-CTE\]/i', $line)) { |
| | | $binary = $initiated = false; |
| | | continue; |
| | | } |
| | | |
| | | // skip irrelevant untagged responses (we have a result already) |
| | |
| | | |
| | | // BASE64 |
| | | if ($mode == 1) { |
| | | $line = rtrim($line, "\t\r\n\0\x0B"); |
| | | $line = preg_replace('|[^a-zA-Z0-9+=/]|', '', $line); |
| | | // create chunks with proper length for base64 decoding |
| | | $line = $prev.$line; |
| | | $length = strlen($line); |