\n" .
_("ERROR: No available IMAP stream.") .
"\n";
error_box($string);
return false;
}
}
/**
* @param stream $imap_stream imap connection resource
* @param string $query imap command
* @param boolean $handle_errors see sqimap_retrieve_imap_response()
* @param array $response empty string, if return = false
* @param array $message empty string, if return = false
* @param boolean $unique_id (since 1.3.0) see sqimap_session_id()
* @param boolean $filter (since 1.4.1 and 1.5.0) see sqimap_fread()
* @param mixed $outputstream (since 1.4.1 and 1.5.0) see sqimap_fread()
* @param boolean $no_return (since 1.4.1 and 1.5.0) see sqimap_fread()
* @return mixed returns false on imap error. displays error message
* if imap stream is not available.
* @since 1.2.3
*/
function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
&$message, $unique_id = false,$filter=false,
$outputstream=false,$no_return=false) {
if ($imap_stream) {
$sid = sqimap_session_id($unique_id);
fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
$tag_uid_a = explode(' ',trim($sid));
$tag = $tag_uid_a[0];
$read = sqimap_read_data ($imap_stream, $tag, $handle_errors, $response,
$message, $query,$filter,$outputstream,$no_return);
if (empty($read)) { //IMAP server dropped its connection
$response = '';
$message = '';
return false;
}
/* retrieve the response and the message */
$response = $response[$tag];
$message = $message[$tag];
if (!empty($read[$tag])) {
return $read[$tag][0];
} else {
return $read[$tag];
}
} else {
global $squirrelmail_language, $color;
set_up_language($squirrelmail_language);
$string = "\n" .
_("ERROR: No available IMAP stream.") .
"\n";
error_box($string);
return false;
}
}
/**
* @param mixed $new_query
* @param string $tag
* @param array $aQuery
* @param boolean $unique_id see sqimap_session_id()
* @since 1.5.0
*/
function sqimap_prepare_pipelined_query($new_query,&$tag,&$aQuery,$unique_id) {
$sid = sqimap_session_id($unique_id);
$tag_uid_a = explode(' ',trim($sid));
$tag = $tag_uid_a[0];
$query = $sid . ' '.$new_query."\r\n";
$aQuery[$tag] = $query;
}
/**
* @param stream $imap_stream imap stream
* @param array $aQueryList
* @param boolean $handle_errors
* @param array $aServerResponse
* @param array $aServerMessage
* @param boolean $unique_id see sqimap_session_id()
* @param boolean $filter see sqimap_fread()
* @param mixed $outputstream see sqimap_fread()
* @param boolean $no_return see sqimap_fread()
* @since 1.5.0
*/
function sqimap_run_pipelined_command ($imap_stream, $aQueryList, $handle_errors,
&$aServerResponse, &$aServerMessage, $unique_id = false,
$filter=false,$outputstream=false,$no_return=false) {
$aResponse = false;
/*
Do not fire all calls at once to the IMAP server but split the calls up
in portions of $iChunkSize. If we do not do that I think we misbehave as
IMAP client or should handle BYE calls if the IMAP server drops the
connection because the number of queries is to large. This isn't tested
but a wild guess how it could work in the field.
After testing it on Exchange 2000 we discovered that a chunksize of 32
was quicker then when we raised it to 128.
*/
$iQueryCount = count($aQueryList);
$iChunkSize = 32;
// array_chunk would also do the job but it's supported from php > 4.2
$aQueryChunks = array();
$iLoops = floor($iQueryCount / $iChunkSize);
if ($iLoops * $iChunkSize != $iQueryCount) ++$iLoops;
if (!function_exists('array_chunk')) { // arraychunk replacement
reset($aQueryList);
for($i=0;$i<$iLoops;++$i) {
for($j=0;$j<$iChunkSize;++$j) {
$key = key($aQueryList);
$aTmp[$key] = $aQueryList[$key];
if (next($aQueryList) === false) break;
}
$aQueryChunks[] = $aTmp;
}
} else {
$aQueryChunks = array_chunk($aQueryList,$iChunkSize,true);
}
for ($i=0;$i<$iLoops;++$i) {
$aQuery = $aQueryChunks[$i];
foreach($aQuery as $tag => $query) {
fputs($imap_stream,$query);
$aResults[$tag] = false;
}
foreach($aQuery as $tag => $query) {
if ($aResults[$tag] == false) {
$aReturnedResponse = sqimap_retrieve_imap_response ($imap_stream, $tag,
$handle_errors, $response, $message, $query,
$filter,$outputstream,$no_return);
foreach ($aReturnedResponse as $returned_tag => $aResponse) {
if (!empty($aResponse)) {
$aResults[$returned_tag] = $aResponse[0];
} else {
$aResults[$returned_tag] = $aResponse;
}
$aServerResponse[$returned_tag] = $response[$returned_tag];
$aServerMessage[$returned_tag] = $message[$returned_tag];
}
}
}
}
return $aResults;
}
/**
* Custom fgets function: gets a line from the IMAP server,
* no matter how big it may be.
* @param stream $imap_stream the stream to read from
* @return string a line
* @since 1.2.8
*/
function sqimap_fgets($imap_stream) {
$read = '';
$buffer = 4096;
$results = '';
$offset = 0;
while (strpos($results, "\r\n", $offset) === false) {
if (!($read = fgets($imap_stream, $buffer))) {
/* this happens in case of an error */
/* reset $results because it's useless */
$results = false;
break;
}
if ( $results != '' ) {
$offset = strlen($results) - 1;
}
$results .= $read;
}
return $results;
}
/**
* @param stream $imap_stream
* @param integer $iSize
* @param boolean $filter
* @param mixed $outputstream stream or 'php://stdout' string
* @param boolean $no_return controls data returned by function
* @return string
* @since 1.4.1
*/
function sqimap_fread($imap_stream,$iSize,$filter=false,
$outputstream=false, $no_return=false) {
if (!$filter || !$outputstream) {
$iBufferSize = $iSize;
} else {
// see php bug 24033. They changed fread behaviour %$^&$%
$iBufferSize = 7800; // multiple of 78 in case of base64 decoding.
}
if ($iSize < $iBufferSize) {
$iBufferSize = $iSize;
}
$iRetrieved = 0;
$results = '';
$sRead = $sReadRem = '';
// NB: fread can also stop at end of a packet on sockets.
while ($iRetrieved < $iSize) {
$sRead = fread($imap_stream,$iBufferSize);
$iLength = strlen($sRead);
$iRetrieved += $iLength ;
$iRemaining = $iSize - $iRetrieved;
if ($iRemaining < $iBufferSize) {
$iBufferSize = $iRemaining;
}
if ($sRead == '') {
$results = false;
break;
}
if ($sReadRem != '') {
$sRead = $sReadRem . $sRead;
$sReadRem = '';
}
if ($filter && $sRead != '') {
// in case the filter is base64 decoding we return a remainder
$sReadRem = $filter($sRead);
}
if ($outputstream && $sRead != '') {
if (is_resource($outputstream)) {
fwrite($outputstream,$sRead);
} else if ($outputstream == 'php://stdout') {
echo $sRead;
}
}
if ($no_return) {
$sRead = '';
} else {
$results .= $sRead;
}
}
return $results;
}
/**
* Obsolete function, inform plugins that use it
* @param stream $imap_stream
* @param string $tag
* @param boolean $handle_errors
* @param array $response
* @param array $message
* @param string $query
* @since 1.1.3
* @deprecated (since 1.5.0) use sqimap_run_command or sqimap_run_command_list instead
*/
function sqimap_read_data_list($imap_stream, $tag, $handle_errors,
&$response, &$message, $query = '') {
global $color, $oTemplate, $squirrelmail_language;
set_up_language($squirrelmail_language);
$string = "\n" .
_("ERROR: Bad function call.") .
"
\n" .
_("Reason:") . ' '.
'There is a plugin installed which make use of the
' .
'SquirrelMail internal function sqimap_read_data_list.
'.
'Please adapt the installed plugin and let it use
'.
'sqimap_run_command or sqimap_run_command_list instead
'.
'The following query was issued:
'.
htmlspecialchars($query) . '
' . "
\n";
error_box($string);
$oTemplate->display('footer.tpl');
exit;
}
/**
* Function to display an error related to an IMAP query.
* @param string title the caption of the error box
* @param string query the query that went wrong
* @param string message_title optional message title
* @param string message optional error message
* @param string $link an optional link to try again
* @return void
* @since 1.5.0
*/
function sqimap_error_box($title, $query = '', $message_title = '', $message = '', $link = '')
{
global $color, $squirrelmail_language;
set_up_language($squirrelmail_language);
$string = "\n" . $title . "
\n";
$cmd = explode(' ',$query);
$cmd= strtolower($cmd[0]);
if ($query != '' && $cmd != 'login')
$string .= _("Query:") . ' ' . htmlspecialchars($query) . '
';
if ($message_title != '')
$string .= $message_title;
if ($message != '')
$string .= htmlspecialchars($message);
$string .= "
\n";
if ($link != '')
$string .= $link;
error_box($string);
}
/**
* Reads the output from the IMAP stream. If handle_errors is set to true,
* this will also handle all errors that are received. If it is not set,
* the errors will be sent back through $response and $message.
* @param stream $imap_stream imap stream
* @param string $tag
* @param boolean $handle_errors handle errors internally or send them in $response and $message.
* @param array $response
* @param array $message
* @param string $query command that can be printed if something fails
* @param boolean $filter see sqimap_fread()
* @param mixed $outputstream see sqimap_fread()
* @param boolean $no_return see sqimap_fread()
* @since 1.5.0
*/
function sqimap_retrieve_imap_response($imap_stream, $tag, $handle_errors,
&$response, &$message, $query = '',
$filter = false, $outputstream = false, $no_return = false) {
global $color, $squirrelmail_language;
$read = '';
if (!is_array($message)) $message = array();
if (!is_array($response)) $response = array();
$aResponse = '';
$resultlist = array();
$data = array();
$sCommand = '';
if (preg_match("/^(\w+)\s*/",$query,$aMatch)) {
$sCommand = strtoupper($aMatch[1]);
} else {
// error reporting (shouldn't happen)
}
$read = sqimap_fgets($imap_stream);
$i = 0;
while ($read) {
$char = $read{0};
switch ($char)
{
case '+':
default:
$read = sqimap_fgets($imap_stream);
break;
case $tag{0}:
{
/* get the command */
$arg = '';
$i = strlen($tag)+1;
$s = substr($read,$i);
if (($j = strpos($s,' ')) || ($j = strpos($s,"\n"))) {
$arg = substr($s,0,$j);
}
$found_tag = substr($read,0,$i-1);
if ($found_tag) {
switch ($arg)
{
case 'OK':
case 'BAD':
case 'NO':
case 'BYE':
case 'PREAUTH':
$response[$found_tag] = $arg;
$message[$found_tag] = trim(substr($read,$i+strlen($arg)));
if (!empty($data)) {
$resultlist[] = $data;
}
$aResponse[$found_tag] = $resultlist;
$data = $resultlist = array();
if ($found_tag == $tag) {
break 3; /* switch switch while */
}
break;
default:
/* this shouldn't happen */
$response[$found_tag] = $arg;
$message[$found_tag] = trim(substr($read,$i+strlen($arg)));
if (!empty($data)) {
$resultlist[] = $data;
}
$aResponse[$found_tag] = $resultlist;
$data = $resultlist = array();
if ($found_tag == $tag) {
break 3; /* switch switch while */
}
}
}
$read = sqimap_fgets($imap_stream);
if ($read === false) { /* error */
break 2; /* switch while */
}
break;
} // end case $tag{0}
case '*':
{
if (($sCommand == "FETCH" || $sCommand == "STORE") && preg_match('/^\*\s\d+\sFETCH/',$read)) {
/* check for literal */
$s = substr($read,-3);
$fetch_data = array();
do { /* outer loop, continue until next untagged fetch
or tagged reponse */
do { /* innerloop for fetching literals. with this loop
we prohibid that literal responses appear in the
outer loop so we can trust the untagged and
tagged info provided by $read */
if ($s === "}\r\n") {
$j = strrpos($read,'{');
$iLit = substr($read,$j+1,-3);
$fetch_data[] = $read;
$sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
if ($sLiteral === false) { /* error */
break 4; /* while while switch while */
}
/* backwards compattibility */
$aLiteral = explode("\n", $sLiteral);
/* release not neaded data */
unset($sLiteral);
foreach ($aLiteral as $line) {
$fetch_data[] = $line ."\n";
}
/* release not neaded data */
unset($aLiteral);
/* next fgets belongs to this fetch because
we just got the exact literalsize and there
must follow data to complete the response */
$read = sqimap_fgets($imap_stream);
if ($read === false) { /* error */
break 4; /* while while switch while */
}
$fetch_data[] = $read;
} else {
$fetch_data[] = $read;
}
/* retrieve next line and check in the while
statements if it belongs to this fetch response */
$read = sqimap_fgets($imap_stream);
if ($read === false) { /* error */
break 4; /* while while switch while */
}
/* check for next untagged reponse and break */
if ($read{0} == '*') break 2;
$s = substr($read,-3);
} while ($s === "}\r\n");
$s = substr($read,-3);
} while ($read{0} !== '*' &&
substr($read,0,strlen($tag)) !== $tag);
$resultlist[] = $fetch_data;
/* release not neaded data */
unset ($fetch_data);
} else {
$s = substr($read,-3);
do {
if ($s === "}\r\n") {
$j = strrpos($read,'{');
$iLit = substr($read,$j+1,-3);
// check for numeric value to avoid that untagged responses like:
// * OK [PARSE] Unexpected characters at end of address: {SET:debug=51}
// will trigger literal fetching ({SET:debug=51} !== int )
if (is_numeric($iLit)) {
$data[] = $read;
$sLiteral = fread($imap_stream,$iLit);
if ($sLiteral === false) { /* error */
$read = false;
break 3; /* while switch while */
}
$data[] = $sLiteral;
$data[] = sqimap_fgets($imap_stream);
} else {
$data[] = $read;
}
} else {
$data[] = $read;
}
$read = sqimap_fgets($imap_stream);
if ($read === false) {
break 3; /* while switch while */
} else if ($read{0} == '*') {
break;
}
$s = substr($read,-3);
} while ($s === "}\r\n");
break 1;
}
break;
} // end case '*'
} // end switch
} // end while
/* error processing in case $read is false */
if ($read === false) {
// try to retrieve an untagged bye respons from the results
$sResponse = array_pop($data);
if ($sResponse !== NULL && strpos($sResponse,'* BYE') !== false) {
if (!$handle_errors) {
$query = '';
}
sqimap_error_box(_("ERROR: IMAP server closed the connection."), $query, _("Server responded:"),$sResponse);
echo '