/** * Send a command to the IMAP server and retrieve * the response * * @param string $command The command to send * * @param string $data If we are issuing a command * that means the server will * consequently expect literal * data sent then this is that data. * * @return mixed Server response string on success * or bool false on error */ function sendCmd($command, $data = null, $fh = false) { if (!is_resource($this->socket->fp)) { $this->lastError = IC_NOT_CONNECTED; return false; } if (feof($this->socket->fp)) { $this->lastError = IC_NOT_CONNECTED; return false; } $cid = $this->cid; $this->socket->writeLine("{$this->cid} {$command}"); $this->cid++; if ($this->debug) { $this->debugOutput("C: {$cid} {$command}\n"); } $resp = ''; $loop = 0; $lines = 0; $bytesRead = 0; $respSize = 0; $code = ''; $fetch = substr($command, 0, 5) == 'FETCH'; $select = substr($command, 0, 6) == 'SELECT'; while (true) { // Check we are still connected if (!is_resource($this->socket->fp)) { return $resp; } if (feof($this->socket->fp)) { $this->lastError = IC_NOT_CONNECTED; return false; } $line = $this->socket->gets(8192); // 8192 same buffer used in Pear Net::POP3 if ($this->debug) { $this->debugOutput("S: {$line}"); } if (preg_match("/^{$cid} (OK|BAD|NO)/", $line, $m)) { $code = $m[1]; } elseif (!$fetch && !$select && preg_match("/^\\* (BAD|NO|BYE)/", $line, $m)) { $code = $m[1]; } // UW-IMAP been brain dead for 'status' command on a mailbox if ($code == 'NO' && preg_match("/NO CLIENT BUG DETECTED/", $line)) { $code = ''; continue; } else { if ($code == 'BAD' || $code == 'NO' || $code == 'BYE') { return false; } else { if ($code == 'OK') { return !empty($resp) ? $resp : $line; } } } $lines++; // If writing to the filehandle, get msg size and skip the first line response from the IMAP server if (is_resource($fh) && preg_match('/^\\* /', $line) && $lines == '1') { preg_match('/\\{(\\d+)\\}/', $line, $m); $respSize = $m[1]; continue; } // Check if we are sending literal data // and server is ready for literal data if ($data && preg_match('/^\\+/', $line)) { $size = $this->socket->write($data); if ($this->debug) { $this->debugOutput("C: {$cid} {$data}\n"); } sleep(1); $this->socket->write("\r\n"); continue; } // Double check that we are not hung on a // command (ie not enough data sent) if ($data && $line == '') { // Send some new lines to try and // reach expected bytes $this->socket->writeLine(''); if ($this->debug) { $this->debugOutput("C: {$cid} \n"); } } // Replace/fix - Under cyrus, multiple IMAP logins, mailbox locks, sockets are closed automatically if ($line == '' && $loop > 1000) { return $resp; } else { if ($line == '') { $loop++; } } // Print to the filehandle only up to $respSize bytes so we do not append the last IMAP header on the message if ($stop) { continue; } elseif (is_resource($fh)) { $bytesRead += strlen($line); // Strip Control-M chars $line = preg_replace('/\\cM+$/', '', $line); fwrite($fh, $line); // Only read up to expected byte size if ($bytesRead == $respSize) { $stop = true; } } else { $resp .= "{$line}"; } } }