| | |
| | | <?php |
| | | |
| | | /** |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | |
| | | 'MDNSENT' => '$MDNSent', |
| | | '*' => '\\*', |
| | | ); |
| | | |
| | | public static $mupdate; |
| | | |
| | | protected $fp; |
| | | protected $host; |
| | |
| | | // folder name with spaces. Let's try to handle this situation |
| | | if (!is_array($items) && ($pos = strpos($response, '(')) !== false) { |
| | | $response = substr($response, $pos); |
| | | $items = $this->tokenizeResponse($response, 1); |
| | | $items = $this->tokenizeResponse($response, 1); |
| | | |
| | | if (!is_array($items)) { |
| | | return $result; |
| | | } |
| | |
| | | * |
| | | * @param string $ref Reference name |
| | | * @param string $mailbox Mailbox name |
| | | * @param array $status_opts (see self::_listMailboxes) |
| | | * @param array $return_opts (see self::_listMailboxes) |
| | | * @param array $select_opts (see self::_listMailboxes) |
| | | * |
| | | * @return array List of mailboxes or hash of options if $status_opts argument |
| | | * is non-empty. |
| | | * @return array|bool List of mailboxes or hash of options if STATUS/MYROGHTS response |
| | | * is requested, False on error. |
| | | */ |
| | | function listMailboxes($ref, $mailbox, $status_opts=array(), $select_opts=array()) |
| | | function listMailboxes($ref, $mailbox, $return_opts=array(), $select_opts=array()) |
| | | { |
| | | return $this->_listMailboxes($ref, $mailbox, false, $status_opts, $select_opts); |
| | | return $this->_listMailboxes($ref, $mailbox, false, $return_opts, $select_opts); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param string $ref Reference name |
| | | * @param string $mailbox Mailbox name |
| | | * @param array $status_opts (see self::_listMailboxes) |
| | | * @param array $return_opts (see self::_listMailboxes) |
| | | * |
| | | * @return array List of mailboxes or hash of options if $status_opts argument |
| | | * is non-empty. |
| | | * @return array|bool List of mailboxes or hash of options if STATUS/MYROGHTS response |
| | | * is requested, False on error. |
| | | */ |
| | | function listSubscribed($ref, $mailbox, $status_opts=array()) |
| | | function listSubscribed($ref, $mailbox, $return_opts=array()) |
| | | { |
| | | return $this->_listMailboxes($ref, $mailbox, true, $status_opts, NULL); |
| | | return $this->_listMailboxes($ref, $mailbox, true, $return_opts, NULL); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param string $ref Reference name |
| | | * @param string $mailbox Mailbox name |
| | | * @param bool $subscribed Enables returning subscribed mailboxes only |
| | | * @param array $status_opts List of STATUS options |
| | | * (RFC5819: LIST-STATUS: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN) |
| | | * or RETURN options (RFC5258: LIST_EXTENDED: SUBSCRIBED, CHILDREN) |
| | | * @param array $return_opts List of RETURN options (RFC5819: LIST-STATUS, RFC5258: LIST-EXTENDED) |
| | | * Possible: MESSAGES, RECENT, UIDNEXT, UIDVALIDITY, UNSEEN, |
| | | * MYRIGHTS, SUBSCRIBED, CHILDREN |
| | | * @param array $select_opts List of selection options (RFC5258: LIST-EXTENDED) |
| | | * Possible: SUBSCRIBED, RECURSIVEMATCH, REMOTE, |
| | | * SPECIAL-USE (RFC6154) |
| | | * |
| | | * @return array List of mailboxes or hash of options if $status_ops argument |
| | | * is non-empty. |
| | | * @return array|bool List of mailboxes or hash of options if STATUS/MYROGHTS response |
| | | * is requested, False on error. |
| | | */ |
| | | protected function _listMailboxes($ref, $mailbox, $subscribed=false, |
| | | $status_opts=array(), $select_opts=array()) |
| | | $return_opts=array(), $select_opts=array()) |
| | | { |
| | | if (!strlen($mailbox)) { |
| | | $mailbox = '*'; |
| | |
| | | $args[] = $this->escape($ref); |
| | | $args[] = $this->escape($mailbox); |
| | | |
| | | if (!empty($status_opts) && $this->getCapability('LIST-EXTENDED')) { |
| | | $rets = array_intersect($status_opts, array('SUBSCRIBED', 'CHILDREN')); |
| | | if (!empty($return_opts) && $this->getCapability('LIST-EXTENDED')) { |
| | | $ext_opts = array('SUBSCRIBED', 'CHILDREN'); |
| | | $rets = array_intersect($return_opts, $ext_opts); |
| | | $return_opts = array_diff($return_opts, $rets); |
| | | } |
| | | |
| | | if (!empty($status_opts) && $this->getCapability('LIST-STATUS')) { |
| | | $status_opts = array_intersect($status_opts, array('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN')); |
| | | if (!empty($return_opts) && $this->getCapability('LIST-STATUS')) { |
| | | $lstatus = true; |
| | | $status_opts = array('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN'); |
| | | $opts = array_diff($return_opts, $status_opts); |
| | | $status_opts = array_diff($return_opts, $opts); |
| | | |
| | | if (!empty($status_opts)) { |
| | | $lstatus = true; |
| | | $rets[] = 'STATUS (' . implode(' ', $status_opts) . ')'; |
| | | } |
| | | |
| | | if (!empty($opts)) { |
| | | $rets = array_merge($rets, $opts); |
| | | } |
| | | } |
| | | |
| | |
| | | $line = substr($response, $last, $pos - $last); |
| | | $last = $pos + 2; |
| | | |
| | | if (!preg_match('/^\* (LIST|LSUB|STATUS) /i', $line, $m)) { |
| | | if (!preg_match('/^\* (LIST|LSUB|STATUS|MYRIGHTS) /i', $line, $m)) { |
| | | continue; |
| | | } |
| | | |
| | | $cmd = strtoupper($m[1]); |
| | | $line = substr($line, strlen($m[0])); |
| | | |
| | |
| | | $this->data['LIST'][$mailbox], $opts)); |
| | | } |
| | | } |
| | | // * STATUS <mailbox> (<result>) |
| | | else if ($cmd == 'STATUS') { |
| | | list($mailbox, $status) = $this->tokenizeResponse($line, 2); |
| | | else if ($lstatus) { |
| | | // * STATUS <mailbox> (<result>) |
| | | if ($cmd == 'STATUS') { |
| | | list($mailbox, $status) = $this->tokenizeResponse($line, 2); |
| | | |
| | | for ($i=0, $len=count($status); $i<$len; $i += 2) { |
| | | list($name, $value) = $this->tokenizeResponse($status, 2); |
| | | $folders[$mailbox][$name] = $value; |
| | | for ($i=0, $len=count($status); $i<$len; $i += 2) { |
| | | list($name, $value) = $this->tokenizeResponse($status, 2); |
| | | $folders[$mailbox][$name] = $value; |
| | | } |
| | | } |
| | | // * MYRIGHTS <mailbox> <acl> |
| | | else if ($cmd == 'MYRIGHTS') { |
| | | list($mailbox, $acl) = $this->tokenizeResponse($line, 2); |
| | | $folders[$mailbox]['MYRIGHTS'] = $acl; |
| | | } |
| | | } |
| | | } |
| | |
| | | $encoding = $encoding ? trim($encoding) : 'US-ASCII'; |
| | | $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES'; |
| | | $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; |
| | | $data = ''; |
| | | |
| | | list($code, $response) = $this->execute($return_uid ? 'UID THREAD' : 'THREAD', |
| | | array($algorithm, $encoding, $criteria)); |
| | |
| | | $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; |
| | | } |
| | |
| | | */ |
| | | protected 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; |
| | | } |
| | | $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); |
| | |
| | | } |
| | | } |
| | | } |
| | | } while (!$this->startsWith($line, $key, true)); |
| | | } while (!$this->startsWith($line, $key, true) || !$initiated); |
| | | |
| | | if ($result !== false) { |
| | | if ($file) { |
| | |
| | | /** |
| | | * Returns QUOTA information |
| | | * |
| | | * @param string $mailbox Mailbox name |
| | | * |
| | | * @return array Quota information |
| | | */ |
| | | function getQuota() |
| | | function getQuota($mailbox = null) |
| | | { |
| | | /* |
| | | * GETQUOTAROOT "INBOX" |
| | | * QUOTAROOT INBOX user/rchijiiwa1 |
| | | * QUOTA user/rchijiiwa1 (STORAGE 654 9765) |
| | | * OK Completed |
| | | */ |
| | | $result = false; |
| | | $quota_lines = array(); |
| | | $key = $this->nextTag(); |
| | | $command = $key . ' GETQUOTAROOT INBOX'; |
| | | |
| | | // get line(s) containing quota info |
| | | if ($this->putLine($command)) { |
| | | do { |
| | | $line = rtrim($this->readLine(5000)); |
| | | if (preg_match('/^\* QUOTA /', $line)) { |
| | | $quota_lines[] = $line; |
| | | } |
| | | } while (!$this->startsWith($line, $key, true, true)); |
| | | } |
| | | else { |
| | | $this->setError(self::ERROR_COMMAND, "Unable to send command: $command"); |
| | | if ($mailbox === null || $mailbox === '') { |
| | | $mailbox = 'INBOX'; |
| | | } |
| | | |
| | | // return false if not found, parse if found |
| | | // a0001 GETQUOTAROOT INBOX |
| | | // * QUOTAROOT INBOX user/sample |
| | | // * QUOTA user/sample (STORAGE 654 9765) |
| | | // a0001 OK Completed |
| | | |
| | | list($code, $response) = $this->execute('GETQUOTAROOT', array($this->escape($mailbox))); |
| | | |
| | | $result = false; |
| | | $min_free = PHP_INT_MAX; |
| | | foreach ($quota_lines as $key => $quota_line) { |
| | | $quota_line = str_replace(array('(', ')'), '', $quota_line); |
| | | $parts = explode(' ', $quota_line); |
| | | $storage_part = array_search('STORAGE', $parts); |
| | | $all = array(); |
| | | |
| | | if (!$storage_part) { |
| | | continue; |
| | | if ($code == self::ERROR_OK) { |
| | | foreach (explode("\n", $response) as $line) { |
| | | if (preg_match('/^\* QUOTA /', $line)) { |
| | | list(, , $quota_root) = $this->tokenizeResponse($line, 3); |
| | | |
| | | while ($line) { |
| | | list($type, $used, $total) = $this->tokenizeResponse($line, 1); |
| | | $type = strtolower($type); |
| | | |
| | | if ($type && $total) { |
| | | $all[$quota_root][$type]['used'] = intval($used); |
| | | $all[$quota_root][$type]['total'] = intval($total); |
| | | } |
| | | } |
| | | |
| | | if (empty($all[$quota_root]['storage'])) { |
| | | continue; |
| | | } |
| | | |
| | | $used = $all[$quota_root]['storage']['used']; |
| | | $total = $all[$quota_root]['storage']['total']; |
| | | $free = $total - $used; |
| | | |
| | | // calculate lowest available space from all storage quotas |
| | | if ($free < $min_free) { |
| | | $min_free = $free; |
| | | $result['used'] = $used; |
| | | $result['total'] = $total; |
| | | $result['percent'] = min(100, round(($used/max(1,$total))*100)); |
| | | $result['free'] = 100 - $result['percent']; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | $used = intval($parts[$storage_part+1]); |
| | | $total = intval($parts[$storage_part+2]); |
| | | $free = $total - $used; |
| | | |
| | | // return lowest available space from all quotas |
| | | if ($free < $min_free) { |
| | | $min_free = $free; |
| | | $result['used'] = $used; |
| | | $result['total'] = $total; |
| | | $result['percent'] = min(100, round(($used/max(1,$total))*100)); |
| | | $result['free'] = 100 - $result['percent']; |
| | | } |
| | | if (!empty($result)) { |
| | | $result['all'] = $all; |
| | | } |
| | | |
| | | return $result; |
| | |
| | | for ($i=0; $i<$size; $i++) { |
| | | if (isset($mbox) && is_array($data[$i])) { |
| | | $size_sub = count($data[$i]); |
| | | for ($x=0; $x<$size_sub; $x++) { |
| | | for ($x=0; $x<$size_sub; $x+=2) { |
| | | if ($data[$i][$x+1] !== null) |
| | | $result[$mbox][$data[$i][$x]] = $data[$i][++$x]; |
| | | $result[$mbox][$data[$i][$x]] = $data[$i][$x+1]; |
| | | } |
| | | unset($data[$i]); |
| | | } |
| | |
| | | } |
| | | } |
| | | else if (isset($mbox)) { |
| | | if ($data[$i+1] !== null) |
| | | $result[$mbox][$data[$i]] = $data[++$i]; |
| | | if ($data[++$i] !== null) |
| | | $result[$mbox][$data[$i-1]] = $data[$i]; |
| | | unset($data[$i]); |
| | | unset($data[$i-1]); |
| | | } |
| | |
| | | } |
| | | |
| | | foreach ($data as $entry) { |
| | | // Workaround cyrus-murder bug, the entry[2] string needs to be escaped |
| | | if (self::$mupdate) { |
| | | $entry[2] = addcslashes($entry[2], '\\"'); |
| | | } |
| | | |
| | | // ANNOTATEMORE drafts before version 08 require quoted parameters |
| | | $entries[] = sprintf('%s (%s %s)', $this->escape($entry[0], true), |
| | | $this->escape($entry[1], true), $this->escape($entry[2], true)); |
| | |
| | | |
| | | if (!isset($this->prefs['literal+']) && in_array('LITERAL+', $this->capability)) { |
| | | $this->prefs['literal+'] = true; |
| | | } |
| | | |
| | | if (preg_match('/(\[| )MUPDATE=.*/', $str)) { |
| | | self::$mupdate = true; |
| | | } |
| | | |
| | | if ($trusted) { |