| | |
| | | private $capability_readed = false; |
| | | private $prefs; |
| | | |
| | | const ERROR_OK = 0; |
| | | const ERROR_NO = -1; |
| | | const ERROR_BAD = -2; |
| | | const ERROR_BYE = -3; |
| | | const ERROR_COMMAND = -5; |
| | | const ERROR_UNKNOWN = -4; |
| | | |
| | | /** |
| | | * Object constructor |
| | | */ |
| | |
| | | return $line; |
| | | } |
| | | |
| | | function parseResult($string) |
| | | function parseResult($string, $err_prefix='') |
| | | { |
| | | $a = explode(' ', trim($string)); |
| | | if (count($a) >= 2) { |
| | | $res = strtoupper($a[1]); |
| | | if (preg_match('/^[a-z0-9*]+ (OK|NO|BAD|BYE)(.*)$/i', trim($string), $matches)) { |
| | | $res = strtoupper($matches[1]); |
| | | $str = trim($matches[2]); |
| | | |
| | | if ($res == 'OK') { |
| | | return 0; |
| | | return self::ERROR_OK; |
| | | } else if ($res == 'NO') { |
| | | return -1; |
| | | $this->errornum = self::ERROR_NO; |
| | | } else if ($res == 'BAD') { |
| | | return -2; |
| | | $this->errornum = self::ERROR_BAD; |
| | | } else if ($res == 'BYE') { |
| | | @fclose($this->fp); |
| | | $this->fp = null; |
| | | return -3; |
| | | $this->errornum = self::ERROR_BYE; |
| | | } |
| | | |
| | | if ($str) |
| | | $this->error = $err_prefix ? $err_prefix.$str : $str; |
| | | |
| | | return $this->errornum; |
| | | } |
| | | return -4; |
| | | return self::ERROR_UNKNOWN; |
| | | } |
| | | |
| | | private function set_error($code, $msg='') |
| | | { |
| | | $this->errornum = $code; |
| | | $this->error = $msg; |
| | | } |
| | | |
| | | // check if $string starts with $match (or * BYE/BAD) |
| | |
| | | |
| | | // process result |
| | | $result = $this->parseResult($line); |
| | | if ($result == 0) { |
| | | $this->errornum = 0; |
| | | if ($result == self::ERROR_OK) { |
| | | return $this->fp; |
| | | } |
| | | |
| | | $this->error = "Authentication for $user failed (AUTH): $line"; |
| | | $this->errornum = $result; |
| | | $this->error = "Authentication for $user failed (AUTH): $line"; |
| | | |
| | | return $result; |
| | | } |
| | |
| | | // process result |
| | | $result = $this->parseResult($line); |
| | | |
| | | if ($result == 0) { |
| | | $this->errornum = 0; |
| | | if ($result == self::ERROR_OK) { |
| | | return $this->fp; |
| | | } |
| | | |
| | | @fclose($this->fp); |
| | | $this->fp = false; |
| | | |
| | | $this->error = "Authentication for $user failed (LOGIN): $line"; |
| | | $this->errornum = $result; |
| | | $this->fp = false; |
| | | $this->error = "Authentication for $user failed (LOGIN): $line"; |
| | | |
| | | return $result; |
| | | } |
| | |
| | | |
| | | // initialize connection |
| | | $this->error = ''; |
| | | $this->errornum = 0; |
| | | $this->errornum = self::ERROR_OK; |
| | | $this->selected = ''; |
| | | $this->user = $user; |
| | | $this->host = $host; |
| | |
| | | |
| | | // check input |
| | | if (empty($host)) { |
| | | $this->error = "Empty host"; |
| | | $this->errornum = -2; |
| | | $this->set_error(self::ERROR_BAD, "Empty host"); |
| | | return false; |
| | | } |
| | | if (empty($user)) { |
| | | $this->error = "Empty user"; |
| | | $this->errornum = -1; |
| | | $this->set_error(self::ERROR_NO, "Empty user"); |
| | | return false; |
| | | } |
| | | if (empty($password)) { |
| | | $this->error = "Empty password"; |
| | | $this->errornum = -1; |
| | | $this->set_error(self::ERROR_NO, "Empty password"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $this->fp = @fsockopen($host, $this->prefs['port'], $errno, $errstr); |
| | | |
| | | if (!$this->fp) { |
| | | $this->error = sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr); |
| | | $this->errornum = -2; |
| | | $this->set_error(self::ERROR_BAD, sprintf("Could not connect to %s:%d: %s", $host, $this->prefs['port'], $errstr)); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $this->error = sprintf("Wrong startup greeting (%s:%d): %s", $host, $this->prefs['port'], $line); |
| | | else |
| | | $this->error = sprintf("Empty startup greeting (%s:%d)", $host, $this->prefs['port']); |
| | | $this->errornum = -2; |
| | | $this->errornum = self::ERROR_BAD; |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | $line = $this->readLine(4096); |
| | | if (!preg_match('/^tls0 OK/', $line)) { |
| | | $this->error = "Server responded to STARTTLS with: $line"; |
| | | $this->errornum = -2; |
| | | $this->set_error(self::ERROR_BAD, "Server responded to STARTTLS with: $line"); |
| | | return false; |
| | | } |
| | | |
| | | if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { |
| | | $this->error = "Unable to negotiate TLS"; |
| | | $this->errornum = -2; |
| | | $this->set_error(self::ERROR_BAD, "Unable to negotiate TLS"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $result = $this->authenticate($user, $password, substr($line,2)); |
| | | |
| | | // stop if server sent BYE response |
| | | if ($result == -3) { |
| | | if ($result == self::ERROR_BYE) { |
| | | return false; |
| | | } |
| | | } |
| | |
| | | return true; |
| | | } |
| | | |
| | | if ($this->putLine("sel1 SELECT \"".$this->escape($mailbox).'"')) { |
| | | do { |
| | | $line = rtrim($this->readLine(512)); |
| | | $command = "sel1 SELECT \"".$this->escape($mailbox).'"'; |
| | | |
| | | if (preg_match('/^\* ([0-9]+) (EXISTS|RECENT)$/', $line, $m)) { |
| | | $token = strtolower($m[2]); |
| | | $this->$token = (int) $m[1]; |
| | | } |
| | | else if (preg_match('/\[?PERMANENTFLAGS\s+\(([^\)]+)\)\]/U', $line, $match)) { |
| | | $this->permanentflags = explode(' ', $match[1]); |
| | | } |
| | | } while (!$this->startsWith($line, 'sel1', true, true)); |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | |
| | | if ($this->parseResult($line) == 0) { |
| | | $this->selected = $mailbox; |
| | | return true; |
| | | } |
| | | } |
| | | do { |
| | | $line = rtrim($this->readLine(512)); |
| | | |
| | | $this->error = "Couldn't select $mailbox"; |
| | | if (preg_match('/^\* ([0-9]+) (EXISTS|RECENT)$/', $line, $m)) { |
| | | $token = strtolower($m[2]); |
| | | $this->$token = (int) $m[1]; |
| | | } |
| | | else if (preg_match('/\[?PERMANENTFLAGS\s+\(([^\)]+)\)\]/U', $line, $match)) { |
| | | $this->permanentflags = explode(' ', $match[1]); |
| | | } |
| | | } while (!$this->startsWith($line, 'sel1', true, true)); |
| | | |
| | | if ($this->parseResult($line, 'SELECT: ') == self::ERROR_OK) { |
| | | $this->selected = $mailbox; |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | |
| | | $command .= ' '.$add; |
| | | |
| | | if (!$this->putLineC($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | do { |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 's ', true, true)); |
| | | |
| | | $result_code = $this->parseResult($line); |
| | | if ($result_code != 0) { |
| | | $this->error = "Sort: $line"; |
| | | $result_code = $this->parseResult($line, 'SORT: '); |
| | | if ($result_code != self::ERROR_OK) { |
| | | return false; |
| | | } |
| | | |
| | |
| | | $request = $key . $request; |
| | | |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | $result = -1; |
| | | if ($this->putLine("fuid FETCH $id (UID)")) { |
| | | do { |
| | | $line = rtrim($this->readLine(1024)); |
| | | if (preg_match("/^\* $id FETCH \(UID (.*)\)/i", $line, $r)) { |
| | | $result = $r[1]; |
| | | } |
| | | } while (!$this->startsWith($line, 'fuid', true, true)); |
| | | } |
| | | $command = "fuid FETCH $id (UID)"; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return -1; |
| | | } |
| | | |
| | | do { |
| | | $line = rtrim($this->readLine(1024)); |
| | | if (preg_match("/^\* $id FETCH \(UID (.*)\)/i", $line, $r)) { |
| | | $result = $r[1]; |
| | | } |
| | | } while (!$this->startsWith($line, 'fuid', true, true)); |
| | | |
| | | return $result; |
| | | } |
| | |
| | | $request .= "LIST-POST DISPOSITION-NOTIFICATION-TO".$add.")])"; |
| | | |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | do { |
| | |
| | | } |
| | | |
| | | $c = 0; |
| | | $command = $messages ? "UID EXPUNGE $messages" : "EXPUNGE"; |
| | | $command = $messages ? "exp1 UID EXPUNGE $messages" : "exp1 EXPUNGE"; |
| | | |
| | | if (!$this->putLine("exp1 $command")) { |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return -1; |
| | | } |
| | | |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 'exp1', true, true)); |
| | | |
| | | if ($this->parseResult($line) == 0) { |
| | | if ($this->parseResult($line, 'EXPUNGE: ') == self::ERROR_OK) { |
| | | $this->selected = ''; // state has changed, need to reselect |
| | | return $c; |
| | | } |
| | | $this->error = $line; |
| | | |
| | | return -1; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | $c = 0; |
| | | if (!$this->putLine("flg UID STORE $messages {$mod}FLAGS ($flag)")) { |
| | | $command = "flg UID STORE $messages {$mod}FLAGS ($flag)"; |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 'flg', true, true)); |
| | | |
| | | if ($this->parseResult($line) == 0) { |
| | | if ($this->parseResult($line, 'STORE: ') == self::ERROR_OK) { |
| | | return $c; |
| | | } |
| | | |
| | | $this->error = $line; |
| | | return -1; |
| | | } |
| | | |
| | |
| | | return -1; |
| | | } |
| | | |
| | | $this->putLine("cpy1 UID COPY $messages \"".$this->escape($to)."\""); |
| | | $line = $this->readReply(); |
| | | return $this->parseResult($line); |
| | | $command = "cpy1 UID COPY $messages \"".$this->escape($to)."\""; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | } |
| | | |
| | | $line = $this->readReply(); |
| | | return $this->parseResult($line, 'COPY: '); |
| | | } |
| | | |
| | | function countUnseen($folder) |
| | |
| | | $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; |
| | | $data = ''; |
| | | |
| | | if (!$this->putLineC("thrd1 THREAD $algorithm $encoding $criteria")) { |
| | | $command = "thrd1 THREAD $algorithm $encoding $criteria"; |
| | | |
| | | if (!$this->putLineC($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | do { |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 'thrd1', true, true)); |
| | | |
| | | $result_code = $this->parseResult($line); |
| | | if ($result_code == 0) { |
| | | $result_code = $this->parseResult($line, 'THREAD: '); |
| | | if ($result_code == self::ERROR_OK) { |
| | | $depthmap = array(); |
| | | $haschildren = array(); |
| | | $tree = $this->parseThread($data, 0, strlen($data), null, null, 0, $depthmap, $haschildren); |
| | | return array($tree, $depthmap, $haschildren); |
| | | } |
| | | |
| | | $this->error = "Thread: $line"; |
| | | return false; |
| | | } |
| | | |
| | |
| | | $query = 'srch1 ' . ($return_uid ? 'UID ' : '') . 'SEARCH ' . trim($criteria); |
| | | |
| | | if (!$this->putLineC($query)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $query"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 'srch1', true, true)); |
| | | |
| | | $result_code = $this->parseResult($line); |
| | | if ($result_code == 0) { |
| | | $result_code = $this->parseResult($line, 'SEARCH: '); |
| | | if ($result_code == self::ERROR_OK) { |
| | | return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY); |
| | | } |
| | | |
| | | $this->error = "Search: $line"; |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | $ref = $this->escape($ref); |
| | | $mailbox = $this->escape($mailbox); |
| | | $query = $key." ".$command." \"". $ref ."\" \"". $mailbox ."\""; |
| | | |
| | | // send command |
| | | if (!$this->putLine($key." ".$command." \"". $ref ."\" \"". $mailbox ."\"")) { |
| | | $this->error = "Couldn't send $command command"; |
| | | if (!$this->putLine($query)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $query"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | if (is_array($folders)) { |
| | | return $folders; |
| | | } else if ($this->parseResult($line) == 0) { |
| | | } else if ($this->parseResult($line, $command.': ') == self::ERROR_OK) { |
| | | return array(); |
| | | } |
| | | |
| | | $this->error = $line; |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | // send request |
| | | if (!$this->putLine($request)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | function createFolder($folder) |
| | | { |
| | | if ($this->putLine('c CREATE "' . $this->escape($folder) . '"')) { |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'c ', true, true)); |
| | | return ($this->parseResult($line) == 0); |
| | | } |
| | | return false; |
| | | $command = 'c CREATE "' . $this->escape($folder) . '"'; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'c ', true, true)); |
| | | |
| | | return ($this->parseResult($line, 'CREATE: ') == self::ERROR_OK); |
| | | } |
| | | |
| | | function renameFolder($from, $to) |
| | | { |
| | | if ($this->putLine('r RENAME "' . $this->escape($from) . '" "' . $this->escape($to) . '"')) { |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'r ', true, true)); |
| | | return ($this->parseResult($line) == 0); |
| | | } |
| | | return false; |
| | | $command = 'r RENAME "' . $this->escape($from) . '" "' . $this->escape($to) . '"'; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'r ', true, true)); |
| | | |
| | | return ($this->parseResult($line, 'RENAME: ') == self::ERROR_OK); |
| | | } |
| | | |
| | | function deleteFolder($folder) |
| | | { |
| | | if ($this->putLine('d DELETE "' . $this->escape($folder). '"')) { |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'd ', true, true)); |
| | | return ($this->parseResult($line) == 0); |
| | | } |
| | | return false; |
| | | $command = 'd DELETE "' . $this->escape($folder). '"'; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | do { |
| | | $line = $this->readLine(300); |
| | | } while (!$this->startsWith($line, 'd ', true, true)); |
| | | |
| | | return ($this->parseResult($line, 'DELETE: ') == self::ERROR_OK); |
| | | } |
| | | |
| | | function clearFolder($folder) |
| | |
| | | |
| | | function subscribe($folder) |
| | | { |
| | | $query = 'sub1 SUBSCRIBE "' . $this->escape($folder). '"'; |
| | | $this->putLine($query); |
| | | $command = 'sub1 SUBSCRIBE "' . $this->escape($folder). '"'; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | |
| | | $line = trim($this->readLine(512)); |
| | | return ($this->parseResult($line) == 0); |
| | | return ($this->parseResult($line, 'SUBSCRIBE: ') == self::ERROR_OK); |
| | | } |
| | | |
| | | function unsubscribe($folder) |
| | | { |
| | | $query = 'usub1 UNSUBSCRIBE "' . $this->escape($folder) . '"'; |
| | | $this->putLine($query); |
| | | $command = 'usub1 UNSUBSCRIBE "' . $this->escape($folder) . '"'; |
| | | |
| | | if (!$this->putLine($command)) { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | return false; |
| | | } |
| | | |
| | | $line = trim($this->readLine(512)); |
| | | return ($this->parseResult($line) == 0); |
| | | return ($this->parseResult($line, 'UNSUBSCRIBE: ') == self::ERROR_OK); |
| | | } |
| | | |
| | | function append($folder, &$message) |
| | |
| | | $line = $this->readLine(512); |
| | | |
| | | if ($line[0] != '+') { |
| | | // $errornum = $this->parseResult($line); |
| | | $this->error = "Cannot write to folder: $line"; |
| | | $this->parseResult($line, 'APPEND: '); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $line = $this->readLine(); |
| | | } while (!$this->startsWith($line, 'a ', true, true)); |
| | | |
| | | $result = ($this->parseResult($line) == 0); |
| | | if (!$result) { |
| | | $this->error = $line; |
| | | } |
| | | return $result; |
| | | return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | } |
| | | |
| | | $this->error = "Couldn't send command \"$request\""; |
| | | return false; |
| | | } |
| | | |
| | |
| | | $in_fp = fopen($path, 'r'); |
| | | } |
| | | if (!$in_fp) { |
| | | $this->error = "Couldn't open $path for reading"; |
| | | $this->set_error(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $line = $this->readLine(512); |
| | | |
| | | if ($line[0] != '+') { |
| | | //$errornum = $this->parseResult($line); |
| | | $this->error = "Cannot write to folder: $line"; |
| | | $this->parseResult($line, 'APPEND: '); |
| | | return false; |
| | | } |
| | | |
| | |
| | | $line = $this->readLine(); |
| | | } while (!$this->startsWith($line, 'a ', true, true)); |
| | | |
| | | $result = ($this->parseResult($line) == 0); |
| | | if (!$result) { |
| | | $this->error = $line; |
| | | } |
| | | |
| | | return $result; |
| | | |
| | | return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $request"); |
| | | } |
| | | |
| | | $this->error = "Couldn't send command \"$request\""; |
| | | return false; |
| | | } |
| | | |
| | |
| | | |
| | | $key = 'F1247'; |
| | | $result = false; |
| | | $command = $key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)"; |
| | | |
| | | if ($this->putLine($key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)")) { |
| | | if ($this->putLine($command)) { |
| | | do { |
| | | $line = $this->readLine(5000); |
| | | $line = $this->multLine($line, true); |
| | |
| | | |
| | | $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -1)); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | |
| | | */ |
| | | $result = false; |
| | | $quota_lines = array(); |
| | | $command = 'QUOT1 GETQUOTAROOT "INBOX"'; |
| | | |
| | | // get line(s) containing quota info |
| | | if ($this->putLine('QUOT1 GETQUOTAROOT "INBOX"')) { |
| | | if ($this->putLine($command)) { |
| | | do { |
| | | $line = rtrim($this->readLine(5000)); |
| | | if (preg_match('/^\* QUOTA /', $line)) { |
| | |
| | | } |
| | | } while (!$this->startsWith($line, 'QUOT1', true, true)); |
| | | } |
| | | else { |
| | | $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | } |
| | | |
| | | // return false if not found, parse if found |
| | | $min_free = PHP_INT_MAX; |