summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--program/include/rcube_imap_generic.php245
2 files changed, 129 insertions, 117 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 32d907bca..2df1a56fe 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG RoundCube Webmail
===========================
+- Parse untagged CAPABILITY response for LOGIN command (#1486742)
- Renamed all php-cli scripts to use .sh extension
- Some files from /bin + spellchecking actions moved to the new 'utils' task
- Added thread tree icons
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index 6ca6c8f5a..1cb4d4cc2 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -30,7 +30,7 @@
* Struct representing an e-mail message header
*
* @package Mail
- * @author Aleksander Machniak <alec@alec.pl>
+ * @author Aleksander Machniak <alec@alec.pl>
*/
class rcube_mail_header
{
@@ -80,7 +80,7 @@ class iilBasicHeader extends rcube_mail_header
* PHP based wrapper class to connect to an IMAP server
*
* @package Mail
- * @author Aleksander Machniak <alec@alec.pl>
+ * @author Aleksander Machniak <alec@alec.pl>
*/
class rcube_imap_generic
{
@@ -118,7 +118,7 @@ class rcube_imap_generic
function __construct()
{
}
-
+
private function putLine($string, $endln=true)
{
if (!$this->fp)
@@ -127,7 +127,7 @@ class rcube_imap_generic
if (!empty($this->prefs['debug_mode'])) {
write_log('imap', 'C: '. rtrim($string));
}
-
+
return fputs($this->fp, $string . ($endln ? "\r\n" : ''));
}
@@ -173,16 +173,16 @@ class rcube_imap_generic
if (!$this->fp) {
return NULL;
}
-
+
if (!$size) {
$size = 1024;
}
-
+
do {
if (feof($this->fp)) {
return $line ? $line : NULL;
}
-
+
$buffer = fgets($this->fp, $size);
if ($buffer === false) {
@@ -204,11 +204,11 @@ class rcube_imap_generic
$line = chop($line);
if (preg_match('/\{[0-9]+\}$/', $line)) {
$out = '';
-
+
preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
$bytes = $a[2][0];
while (strlen($out) < $bytes) {
- $line = $this->readBytes($bytes);
+ $line = $this->readBytes($bytes);
if ($line === NULL)
break;
$out .= $line;
@@ -216,7 +216,7 @@ class rcube_imap_generic
$line = $a[1][0] . '"' . ($escape ? $this->Escape($out) : $out) . '"';
}
-
+
return $line;
}
@@ -237,17 +237,23 @@ class rcube_imap_generic
}
$len = $data_len;
}
-
+
return $data;
}
// don't use it in loops, until you exactly know what you're doing
- private function readReply()
+ private function readReply($untagged=null)
{
do {
$line = trim($this->readLine(1024));
+ // store untagged response lines
+ if ($line[0] == '*')
+ $untagged[] = $line;
} while ($line[0] == '*');
+ if ($untagged)
+ $untagged = join("\n", $untagged);
+
return $line;
}
@@ -331,7 +337,7 @@ class rcube_imap_generic
return false;
}
- // get capabilities (only once) because initial
+ // get capabilities (only once) because initial
// optional CAPABILITY response may differ
$this->capability = array();
@@ -348,7 +354,7 @@ class rcube_imap_generic
}
}
} while ($a[0] != 'cp01');
-
+
$this->capability_readed = true;
if (in_array($name, $this->capability)) {
@@ -368,7 +374,7 @@ class rcube_imap_generic
{
$ipad = '';
$opad = '';
-
+
// initialize ipad, opad
for ($i=0; $i<64; $i++) {
$ipad .= chr(0x36);
@@ -380,17 +386,17 @@ class rcube_imap_generic
for ($i=0; $i<$padLen; $i++) {
$pass .= chr(0);
}
-
+
// generate hash
$hash = md5($this->_xor($pass,$opad) . pack("H*", md5($this->_xor($pass, $ipad) . base64_decode($encChallenge))));
-
+
// generate reply
$reply = base64_encode($user . ' ' . $hash);
-
+
// send result, get reply
$this->putLine($reply);
$line = $this->readLine(1024);
-
+
// process result
$result = $this->parseResult($line);
if ($result == 0) {
@@ -408,7 +414,12 @@ class rcube_imap_generic
{
$this->putLine('a001 LOGIN "'.$this->escape($user).'" "'.$this->escape($password).'"');
- $line = $this->readReply();
+ $line = $this->readReply($untagged);
+
+ // re-set capabilities list if untagged CAPABILITY response provided
+ if (preg_match('/\* CAPABILITY (.+)/i', $untagged, $matches)) {
+ $this->capability = explode(' ', strtoupper($matches[1]));
+ }
// process result
$result = $this->parseResult($line);
@@ -420,7 +431,7 @@ class rcube_imap_generic
@fclose($this->fp);
$this->fp = false;
-
+
$this->error = "Authentication for $user failed (LOGIN): $line";
$this->errornum = $result;
@@ -433,11 +444,11 @@ class rcube_imap_generic
$this->rootdir = $this->prefs['rootdir'];
return true;
}
-
+
if (!$this->getCapability('NAMESPACE')) {
return false;
}
-
+
if (!$this->putLine("ns1 NAMESPACE")) {
return false;
}
@@ -463,12 +474,12 @@ class rcube_imap_generic
if (count($first_userspace)!=2) {
return false;
}
-
+
$this->rootdir = $first_userspace[0];
$this->delimiter = $first_userspace[1];
$this->prefs['rootdir'] = substr($this->rootdir, 0, -1);
$this->prefs['delimiter'] = $this->delimiter;
-
+
return true;
}
@@ -478,8 +489,8 @@ class rcube_imap_generic
* INBOX.foo -> .
* INBOX/foo -> /
* INBOX\foo -> \
- *
- * @return mixed A delimiter (string), or false.
+ *
+ * @return mixed A delimiter (string), or false.
* @see connect()
*/
function getHierarchyDelimiter()
@@ -497,7 +508,7 @@ class rcube_imap_generic
if (!$this->putLine('ghd LIST "" ""')) {
return false;
}
-
+
do {
$line = $this->readLine(500);
if ($line[0] == '*') {
@@ -531,7 +542,7 @@ class rcube_imap_generic
if (!is_array($data)) {
return false;
}
-
+
// extract user space data (opposed to global/shared space)
$user_space_data = $data[0];
if (!is_array($user_space_data)) {
@@ -545,7 +556,7 @@ class rcube_imap_generic
}
// extract delimiter
- $delimiter = $first_userspace[1];
+ $delimiter = $first_userspace[1];
return $delimiter;
}
@@ -566,7 +577,7 @@ class rcube_imap_generic
$message = "INITIAL: $auth_method\n";
$result = false;
-
+
// initialize connection
$this->error = '';
$this->errornum = 0;
@@ -648,7 +659,7 @@ class rcube_imap_generic
$this->errornum = -2;
return false;
}
-
+
// Now we're authenticated, capabilities need to be reread
$this->clearCapability();
}
@@ -675,18 +686,18 @@ class rcube_imap_generic
if ($line[0] == '+') {
// got a challenge string, try CRAM-MD5
$result = $this->authenticate($user, $password, substr($line,2));
-
+
// stop if server sent BYE response
if ($result == -3) {
return false;
}
}
-
+
if (!is_resource($result) && $orig_method == 'CHECK') {
$auth_method = 'PLAIN';
}
}
-
+
if ($auth_method == 'PLAIN') {
// do plain text auth
$result = $this->login($user, $password);
@@ -727,7 +738,7 @@ class rcube_imap_generic
if ($this->selected == $mailbox) {
return true;
}
-
+
if ($this->putLine("sel1 SELECT \"".$this->escape($mailbox).'"')) {
do {
$line = chop($this->readLine(300));
@@ -763,7 +774,7 @@ class rcube_imap_generic
if (empty($mailbox)) {
$mailbox = 'INBOX';
}
-
+
$this->select($mailbox);
if ($this->selected == $mailbox) {
return $this->recent;
@@ -777,7 +788,7 @@ class rcube_imap_generic
if ($refresh) {
$this->selected = '';
}
-
+
$this->select($mailbox);
if ($this->selected == $mailbox) {
return $this->exists;
@@ -792,7 +803,7 @@ class rcube_imap_generic
if ($field == 'INTERNALDATE') {
$field = 'ARRIVAL';
}
-
+
$fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1,
'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
@@ -804,9 +815,9 @@ class rcube_imap_generic
if (!$this->select($mailbox)) {
return false;
}
-
+
$is_uid = $is_uid ? 'UID ' : '';
-
+
// message IDs
if (is_array($add))
$add = $this->compressMessageSet(join(',', $add));
@@ -828,14 +839,14 @@ class rcube_imap_generic
$data .= $line;
}
} while (!$this->startsWith($line, 's ', true, true));
-
+
$result_code = $this->parseResult($line);
-
+
if ($result_code != 0) {
$this->error = "Sort: $line";
return false;
}
-
+
return preg_split('/\s+/', $data, -1, PREG_SPLIT_NO_EMPTY);
}
@@ -851,9 +862,9 @@ class rcube_imap_generic
return false;
}
}
-
+
$index_field = empty($index_field) ? 'DATE' : strtoupper($index_field);
-
+
$fields_a['DATE'] = 1;
$fields_a['INTERNALDATE'] = 4;
$fields_a['ARRIVAL'] = 4;
@@ -877,7 +888,7 @@ class rcube_imap_generic
if (!$this->select($mailbox)) {
return false;
}
-
+
// build FETCH command string
$key = 'fhi0';
$cmd = $uidfetch ? 'UID FETCH' : 'FETCH';
@@ -912,7 +923,7 @@ class rcube_imap_generic
if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) {
$id = $m[1];
$flags = NULL;
-
+
if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) {
$flags = explode(' ', strtoupper($matches[1]));
if (in_array('\\DELETED', $flags)) {
@@ -962,28 +973,28 @@ class rcube_imap_generic
}
} while (!$this->startsWith($line, $key, true, true));
- return $result;
+ return $result;
}
private function compressMessageSet($message_set)
{
- // given a comma delimited list of independent mid's,
+ // given a comma delimited list of independent mid's,
// compresses by grouping sequences together
-
+
// if less than 255 bytes long, let's not bother
if (strlen($message_set)<255) {
return $message_set;
}
-
+
// see if it's already been compress
if (strpos($message_set, ':') !== false) {
return $message_set;
}
-
+
// separate, then sort
$ids = explode(',', $message_set);
sort($ids);
-
+
$result = array();
$start = $prev = $ids[0];
@@ -1006,7 +1017,7 @@ class rcube_imap_generic
} else {
$result[] = $start.':'.$prev;
}
-
+
// return as comma separated string
return implode(',', $result);
}
@@ -1051,14 +1062,14 @@ class rcube_imap_generic
$message_set = join(',', $message_set);
else if (empty($message_set))
$message_set = '1:*';
-
+
return $this->fetchHeaderIndex($mailbox, $message_set, 'UID', false);
}
function fetchHeaders($mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='')
{
$result = array();
-
+
if (!$this->select($mailbox)) {
return false;
}
@@ -1089,15 +1100,15 @@ class rcube_imap_generic
do {
$line = $this->readLine(1024);
$line = $this->multLine($line);
-
+
if (!$line)
break;
-
+
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[2] == 'FETCH')) {
$id = $a[1];
-
+
$result[$id] = new rcube_mail_header;
$result[$id]->id = $id;
$result[$id]->subject = '';
@@ -1133,16 +1144,16 @@ class rcube_imap_generic
}
$time_str = str_replace('"', '', $time_str);
-
+
// if time is gmt...
$time_str = str_replace('GMT','+0000',$time_str);
-
+
$result[$id]->internaldate = $time_str;
$result[$id]->timestamp = $this->StrToTime($time_str);
$result[$id]->date = $time_str;
}
- // BODYSTRUCTURE
+ // BODYSTRUCTURE
if($bodystr) {
while (!preg_match('/ BODYSTRUCTURE (.*) BODY\[HEADER.FIELDS/s', $line, $m)) {
$line2 = $this->readLine(1024);
@@ -1168,13 +1179,13 @@ class rcube_imap_generic
// So, we'll read ahead, and if the one we're reading now is a valid header, we'll
// process the previous line. Otherwise, we'll keep adding the strings until we come
// to the next valid header line.
-
+
do {
$line = chop($this->readLine(300), "\r\n");
// The preg_match below works around communigate imap, which outputs " UID <number>)".
// Without this, the while statement continues on and gets the "FH0 OK completed" message.
- // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.
+ // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.
// This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
// If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
// An alternative might be:
@@ -1198,7 +1209,7 @@ class rcube_imap_generic
// patch from "Maksim Rubis" <siburny@hotmail.com>
} while ($line[0] != ')' && !$this->startsWith($line, $key, true));
- if (strncmp($line, $key, strlen($key))) {
+ if (strncmp($line, $key, strlen($key))) {
// process header, fill rcube_mail_header obj.
// initialize
if (is_array($headers)) {
@@ -1211,10 +1222,10 @@ class rcube_imap_generic
// create array with header field:data
while ( list($lines_key, $str) = each($lines) ) {
list($field, $string) = $this->splitHeaderLine($str);
-
+
$field = strtolower($field);
$string = preg_replace('/\n\s*/', ' ', $string);
-
+
switch ($field) {
case 'date';
$result[$id]->date = $string;
@@ -1280,7 +1291,7 @@ class rcube_imap_generic
if (!empty($flags_str)) {
$flags_str = preg_replace('/[\\\"]/', '', $flags_str);
$flags_a = explode(' ', $flags_str);
-
+
if (is_array($flags_a)) {
// reset($flags_a);
foreach($flags_a as $flag) {
@@ -1343,12 +1354,12 @@ class rcube_imap_generic
$c = count($a);
if ($c > 0) {
-
+
// Strategy:
// First, we'll create an "index" array.
- // Then, we'll use sort() on that array,
+ // Then, we'll use sort() on that array,
// and use that to sort the main array.
-
+
// create "index" array
$index = array();
reset($a);
@@ -1366,7 +1377,7 @@ class rcube_imap_generic
}
$index[$key]=$data;
}
-
+
// sort index
$i = 0;
if ($flag == 'ASC') {
@@ -1375,7 +1386,7 @@ class rcube_imap_generic
arsort($index);
}
- // form new array based on index
+ // form new array based on index
$result = array();
reset($index);
while (list($key, $val) = each($index)) {
@@ -1383,7 +1394,7 @@ class rcube_imap_generic
$i++;
}
}
-
+
return $result;
}
@@ -1392,7 +1403,7 @@ class rcube_imap_generic
if (!$this->select($mailbox)) {
return -1;
}
-
+
$c = 0;
$command = $messages ? "UID EXPUNGE $messages" : "EXPUNGE";
@@ -1406,7 +1417,7 @@ class rcube_imap_generic
$c++;
}
} while (!$this->startsWith($line, 'exp1', true, true));
-
+
if ($this->parseResult($line) == 0) {
$this->selected = ''; // state has changed, need to reselect
return $c;
@@ -1420,13 +1431,13 @@ class rcube_imap_generic
if ($mod != '+' && $mod != '-') {
return -1;
}
-
+
$flag = $this->flags[strtoupper($flag)];
-
+
if (!$this->select($mailbox)) {
return -1;
}
-
+
$c = 0;
if (!$this->putLine("flg UID STORE $messages {$mod}FLAGS ($flag)")) {
return false;
@@ -1464,11 +1475,11 @@ class rcube_imap_generic
if (empty($from) || empty($to)) {
return -1;
}
-
+
if (!$this->select($from)) {
return -1;
}
-
+
$this->putLine("cpy1 UID COPY $messages \"".$this->escape($to)."\"");
$line = $this->readReply();
return $this->parseResult($line);
@@ -1529,7 +1540,7 @@ class rcube_imap_generic
$node += $this->parseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren);
}
}
-
+
return $node;
}
@@ -1542,7 +1553,7 @@ class rcube_imap_generic
$encoding = $encoding ? trim($encoding) : 'US-ASCII';
$algorithm = $algorithm ? trim($algorithm) : 'REFERENCES';
$criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL';
-
+
if (!$this->putLineC("thrd1 THREAD $algorithm $encoding $criteria")) {
return false;
}
@@ -1562,7 +1573,7 @@ class rcube_imap_generic
}
$this->error = "Thread: $line";
- return false;
+ return false;
}
function search($folder, $criteria, $return_uid=false)
@@ -1593,7 +1604,7 @@ class rcube_imap_generic
}
$this->error = "Search: $line";
- return false;
+ return false;
}
function move($messages, $from, $to)
@@ -1601,7 +1612,7 @@ class rcube_imap_generic
if (!$from || !$to) {
return -1;
}
-
+
$r = $this->copy($messages, $from, $to);
if ($r==0) {
@@ -1625,11 +1636,11 @@ class rcube_imap_generic
if (empty($mailbox)) {
$mailbox = '*';
}
-
+
if (empty($ref) && $this->rootdir) {
$ref = $this->rootdir;
}
-
+
if ($subscribed) {
$key = 'lsb';
$command = 'LSUB';
@@ -1644,7 +1655,7 @@ class rcube_imap_generic
$this->error = "Couldn't send $command command";
return false;
}
-
+
// get folder list
do {
$line = $this->readLine(500);
@@ -1677,7 +1688,7 @@ class rcube_imap_generic
if (!$this->select($mailbox)) {
return false;
}
-
+
$result = false;
$parts = (array) $parts;
$key = 'fmh0';
@@ -1688,14 +1699,14 @@ class rcube_imap_generic
// format request
foreach($parts as $part)
$peeks[] = "BODY.PEEK[$part.$type]";
-
+
$request = "$key FETCH $id (" . implode(' ', $peeks) . ')';
// send request
if (!$this->putLine($request)) {
return false;
}
-
+
do {
$line = $this->readLine(1000);
$line = $this->multLine($line);
@@ -1740,7 +1751,7 @@ class rcube_imap_generic
default:
$mode = 0;
}
-
+
$reply_key = '* ' . $id;
$result = false;
@@ -1788,11 +1799,11 @@ class rcube_imap_generic
$sizeStr = substr($line, $from, $len);
$bytes = (int)$sizeStr;
$prev = '';
-
+
while ($bytes > 0) {
$line = $this->readLine(1024);
$len = strlen($line);
-
+
if ($len > $bytes) {
$line = substr($line, 0, $bytes);
$len = strlen($line);
@@ -1811,7 +1822,7 @@ class rcube_imap_generic
}
else
$prev = '';
-
+
if ($file)
fwrite($file, base64_decode($line));
else if ($print)
@@ -1847,7 +1858,7 @@ class rcube_imap_generic
}
}
}
-
+
// read in anything up until last line
if (!$end)
do {
@@ -1922,7 +1933,7 @@ class rcube_imap_generic
{
$query = 'usub1 UNSUBSCRIBE "' . $this->escape($folder) . '"';
$this->putLine($query);
-
+
$line = trim($this->readLine(512));
return ($this->parseResult($line) == 0);
}
@@ -1959,7 +1970,7 @@ class rcube_imap_generic
do {
$line = $this->readLine();
} while (!$this->startsWith($line, 'a ', true, true));
-
+
$result = ($this->parseResult($line) == 0);
if (!$result) {
$this->error = $line;
@@ -1976,17 +1987,17 @@ class rcube_imap_generic
if (!$folder) {
return false;
}
-
+
// open message file
$in_fp = false;
if (file_exists(realpath($path))) {
$in_fp = fopen($path, 'r');
}
- if (!$in_fp) {
+ if (!$in_fp) {
$this->error = "Couldn't open $path for reading";
return false;
}
-
+
$len = filesize($path);
if (!$len) {
return false;
@@ -2036,7 +2047,7 @@ class rcube_imap_generic
return $result;
}
-
+
$this->error = "Couldn't send command \"$request\"";
return false;
}
@@ -2074,7 +2085,7 @@ class rcube_imap_generic
*/
$result = false;
$quota_lines = array();
-
+
// get line(s) containing quota info
if ($this->putLine('QUOT1 GETQUOTAROOT "INBOX"')) {
do {
@@ -2084,24 +2095,24 @@ class rcube_imap_generic
}
} while (!$this->startsWith($line, 'QUOT1', true, true));
}
-
+
// return false if not found, parse if found
$min_free = PHP_INT_MAX;
foreach ($quota_lines as $key => $quota_line) {
$quota_line = preg_replace('/[()]/', '', $quota_line);
$parts = explode(' ', $quota_line);
$storage_part = array_search('STORAGE', $parts);
-
+
if (!$storage_part)
continue;
-
+
$used = intval($parts[$storage_part+1]);
$total = intval($parts[$storage_part+2]);
- $free = $total - $used;
-
+ $free = $total - $used;
+
// return lowest available space from all quotas
- if ($free < $min_free) {
- $min_free = $free;
+ if ($free < $min_free) {
+ $min_free = $free;
$result['used'] = $used;
$result['total'] = $total;
$result['percent'] = min(100, round(($used/max(1,$total))*100));
@@ -2137,7 +2148,7 @@ class rcube_imap_generic
$ts = (int) $ts;
- return $ts < 0 ? 0 : $ts;
+ return $ts < 0 ? 0 : $ts;
}
private function SplitHeaderLine($string)
@@ -2162,7 +2173,7 @@ class rcube_imap_generic
$data = array();
$in_quotes = false;
$elem = 0;
-
+
for ($i;$i<$len;$i++) {
$c = (string)$str[$i];
if ($c == '(' && !$in_quotes) {
@@ -2185,18 +2196,18 @@ class rcube_imap_generic
$data[$elem].=$c;
}
}
-
+
return $data;
}
private function escape($string)
{
- return strtr($string, array('"'=>'\\"', '\\' => '\\\\'));
+ return strtr($string, array('"'=>'\\"', '\\' => '\\\\'));
}
private function unEscape($string)
{
- return strtr($string, array('\\"'=>'"', '\\\\' => '\\'));
+ return strtr($string, array('\\"'=>'"', '\\\\' => '\\'));
}
}