Source for file imap_general.php
Documentation is available at imap_general.php
* This implements all functions that do general IMAP functions.
* @copyright © 1999-2006 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id: imap_general.php,v 1.140.2.37 2006/10/04 19:35:42 stekkel Exp $
require_once(SM_PATH .
'functions/page_header.php');
require_once(SM_PATH .
'functions/auth.php');
* Generates a new session ID by incrementing the last one used;
* this ensures that each command has a unique ID.
* @return string IMAP session id of the form 'A000'.
static $sqimap_session_id =
1;
return( sprintf("A%03d", $sqimap_session_id++
) );
return( sprintf("A%03d", $sqimap_session_id++
) .
' UID' );
* Both send a command and accept the result from the command.
* This is to allow proper session number handling.
function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id =
false) {
fputs ($imap_stream, $sid .
' ' .
$query .
"\r\n");
global $squirrelmail_language, $color;
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: No available IMAP stream.") .
&$message, $unique_id =
false,$filter=
false,
$outputstream=
false,$no_return=
false) {
fputs ($imap_stream, $sid .
' ' .
$query .
"\r\n");
$message, $query,$filter,$outputstream,$no_return);
global $squirrelmail_language, $color;
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: No available IMAP stream.") .
* 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
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 */
$offset =
strlen($results) -
1;
$outputstream=
false, $no_return=
false) {
if (!$filter ||
!$outputstream) {
// see php bug 24033. They changed fread behaviour %$^&$%
$iBufferSize =
7800; // multiple of 78 in case of base64 decoding.
if ($iSize <
$iBufferSize) {
// NB: fread can also stop at end of a packet on sockets.
while ($iRetrieved <
$iSize) {
$sRead =
fread($imap_stream,$iBufferSize);
$iRetrieved +=
$iLength ;
$iRemaining =
$iSize -
$iRetrieved;
if ($iRemaining <
$iBufferSize) {
$iBufferSize =
$iRemaining;
$sRead =
$sReadRem .
$sRead;
if ($filter &&
$sRead !=
'') {
// in case the filter is base64 decoding we return a remainder
$sReadRem =
$filter($sRead);
if ($outputstream &&
$sRead !=
'') {
} else if ($outputstream ==
'php://stdout') {
* 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.
&$response, &$message, $query =
'',
$filter =
false, $outputstream =
false, $no_return =
false) {
global $color, $squirrelmail_language;
$found_tag =
substr($read,0,$i-
1);
if ($arg &&
$found_tag==
$tag) {
break 3; /* switch switch while */
/* this shouldn't happen */
break 3; /* switch switch while */
} elseif($found_tag !==
$tag) {
/* reset data array because we do not need this reponse */
do
{ /* outer loop, continue until next untagged fetch
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 */
$iLit =
substr($read,$j+
1,-
3);
$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 */
foreach ($aLiteral as $line) {
$fetch_data[] =
$line .
"\n";
/* release not neaded data */
/* next fgets belongs to this fetch because
we just got the exact literalsize and there
must follow data to complete the response */
if ($read ===
false) { /* error */
break 4; /* while while switch while */
/* retrieve next line and check in the while
statements if it belongs to this fetch response */
if ($read ===
false) { /* error */
break 4; /* while while switch while */
/* check for next untagged reponse and break */
if ($read{0} ==
'*') break 2;
} while ($s ===
"}\r\n");
} while ($read{0} !==
'*' &&
$resultlist[] =
$fetch_data;
/* release not neaded data */
$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 )
$sLiteral =
fread($imap_stream,$iLit);
if ($sLiteral ===
false) { /* error */
break 3; /* while switch while */
break 3; /* while switch while */
} else if ($read{0} ==
'*') {
} while ($s ===
"}\r\n");
/* error processing in case $read is false */
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Connection dropped by IMAP server.") .
if ($query !=
'' &&
$cmd !=
'login') {
.
'<br />' .
"</font><br />\n";
/* Set $resultlist array */
elseif (empty($resultlist)) {
/* Return result or handle errors */
if ($handle_errors ==
false) {
/* ignore this error from M$ exchange, it is not fatal (aka bug) */
if (strstr($message, 'command resulted in') ===
false) {
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Could not complete request.") .
_("Reason Given:") .
' ' .
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Bad or malformed request.") .
_("Server responded:") .
' ' .
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: IMAP server closed the connection.") .
_("Server responded:") .
' ' .
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Unknown IMAP response.") .
_("Server responded:") .
' ' .
/* the error is displayed but because we don't know the reponse we
return the result anyway */
&$response, &$message, $query =
'',
$filter=
false,$outputstream=
false,$no_return=
false) {
$response, $message, $query,$filter,$outputstream,$no_return);
/* sqimap_read_data should be called for one response
but since it just calls sqimap_read_data_list which
handles multiple responses we need to check for that
and merge the $res array IF they are seperated and
IF it was a FETCH response. */
// if (isset($res[1]) && is_array($res[1]) && isset($res[1][0])
// && preg_match('/^\* \d+ FETCH/', $res[1][0])) {
// foreach($res as $index=>$value) {
// $result = array_merge($result, $res["$index"]);
* Logs the user into the IMAP server. If $hide is set, no error messages
* will be displayed. This function returns the IMAP connection handle.
function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
if (!isset
($onetimepad) ||
empty($onetimepad)) {
sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION );
$host=
$imap_server_address;
/* Use TLS by prefixing "tls://" to the hostname */
$imap_server_address =
'tls://' .
$imap_server_address;
$imap_stream =
@fsockopen($imap_server_address, $imap_port, $error_number, $error_string, 15);
/* Do some error correction */
require_once(SM_PATH .
'functions/display_messages.php');
"<br />\r\n$error_number : $error_string<br />\r\n",
sprintf(_("Error connecting to IMAP server: %s."), $imap_server_address) );
$server_info =
fgets ($imap_stream, 1024);
/* Decrypt the password */
if (($imap_auth_mech ==
'cram-md5') OR ($imap_auth_mech ==
'digest-md5')) {
// We're using some sort of authentication OTHER than plain or login
if ($imap_auth_mech ==
'digest-md5') {
$query =
$tag .
" AUTHENTICATE DIGEST-MD5\r\n";
} elseif ($imap_auth_mech ==
'cram-md5') {
$query =
$tag .
" AUTHENTICATE CRAM-MD5\r\n";
fputs($imap_stream,$query);
// Trim the "+ " off the front
if ($response[0] ==
'+') {
if ($imap_auth_mech ==
'digest-md5') {
} elseif ($imap_auth_mech ==
'cram-md5') {
fputs($imap_stream,$reply);
if ($imap_auth_mech ==
'digest-md5') {
// DIGEST-MD5 has an extra step..
if (substr($read,0,1) ==
'+') { // OK so far..
fputs($imap_stream,"\r\n");
// Fake the response, so the error trap at the bottom will work
$message=
'IMAP server does not appear to support the authentication method selected.';
$message .=
' Please contact your system administrator.';
} elseif ($imap_auth_mech ==
'login') {
// this is a workaround to alert users of LOGINDISABLED, which is done "right" in
// devel but requires functions not available in stable. RFC requires us to
// not send LOGIN when LOGINDISABLED is advertised.
if(stristr($server_info, 'LOGINDISABLED')) {
$message =
_("The IMAP server is reporting that plain text logins are disabled.").
' '.
_("Using CRAM-MD5 or DIGEST-MD5 authentication instead may work.").
' ';
$message .=
_("Also, the use of TLS may allow SquirrelMail to login.").
' ';
$message .=
_("Please contact your system administrator and report this error.");
// Original IMAP login code
$query .=
' {' .
strlen($username) .
"}\r\n$username";
$query .=
' {' .
strlen($password) .
"}\r\n$password";
} elseif ($imap_auth_mech ==
'plain') {
/* Replace this with SASL PLAIN if it ever gets implemented */
$message=
'SquirrelMail does not support SASL PLAIN yet. Rerun conf.pl and use login instead.';
$message=
"Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
/* If the connection was not successful, lets see why */
/* "BAD" and anything else gets reported here. */
require_once(SM_PATH .
'functions/display_messages.php');
if ($response ==
'BAD') {
$string =
sprintf (_("Bad request: %s").
"<br />\r\n", $message);
$string =
sprintf (_("Unknown error: %s") .
"<br />\n", $message);
$string .=
'<br />' .
_("Read data:") .
"<br />\n";
foreach ($read as $line) {
* If the user does not log in with the correct
* username and password it is not possible to get the
* correct locale from the user's preferences.
* Therefore, apply the same hack as on the login
* $squirrelmail_language is set by a cookie when
* the user selects language and logs out
include_once(SM_PATH .
'functions/display_messages.php' );
/* terminate the session nicely */
* Simply logs out the IMAP session
* @param stream imap_stream the IMAP connection to log out.
/* Logout is not valid until the server returns 'BYE'
* If we don't have an imap_stream we're already logged out */
if(isset
($imap_stream) &&
$imap_stream)
* Retreive the CAPABILITY string from the IMAP server.
* If capability is set, returns only that specific capability,
* else returns array of all capabilities.
global $sqimap_capabilities;
for ($i=
2; $i <
count($c); $i++
) {
if (isset
($cap_list[1])) {
// FIX ME. capabilities can occure multiple times.
// THREAD=REFERENCES THREAD=ORDEREDSUBJECT
$sqimap_capabilities[$cap_list[0]] =
$cap_list[1];
$sqimap_capabilities[$cap_list[0]] =
TRUE;
if (isset
($sqimap_capabilities[$capability])) {
return $sqimap_capabilities[$capability];
return $sqimap_capabilities;
* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test
/* Use configured delimiter if set */
if((!empty($optional_delimiter)) &&
$optional_delimiter !=
'detect') {
return $optional_delimiter;
/* Do some caching here */
if (!$sqimap_delimiter) {
* According to something that I can't find, this is supposed to work on all systems
* OS: This won't work in Courier IMAP.
* OS: According to rfc2342 response from NAMESPACE command is:
* OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
* OS: We want to lookup all personal NAMESPACES...
if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
while (list
($k, $v) =
each($pna)) {
$sqimap_delimiter =
$pn[0];
fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
$quote_position =
strpos ($read[0], '"');
$sqimap_delimiter =
substr ($read[0], $quote_position+
1, 1);
return $sqimap_delimiter;
* Gets the number of messages in the current mailbox.
$read_ary =
sqimap_run_command ($imap_stream, "EXAMINE \"$mailbox\"", false, $result, $message);
for ($i =
0; $i <
count($read_ary); $i++
) {
if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
return false; //"BUG! Couldn't get number of messages in $mailbox!";
$aSpecials =
array('(' ,'<' ,',' ,';' ,':');
$aReplace =
array(' (',' <',' ,',' ;',' :');
$iEnd =
strpos($address,'>',$i+
1);
$sToken =
substr($address,$i);
$sToken =
substr($address,$i,$iEnd -
$i +
1);
$iEnd =
strpos($address,$cChar,$i+
1);
$prev_char =
$address{$iEnd-
1};
while ($prev_char ===
'\\' &&
substr($address,$iEnd-
2,2) !==
'\\\\') {
$iEnd =
strpos($address,$cChar,$iEnd+
1);
$prev_char =
$address{$iEnd-
1};
$sToken =
substr($address,$i);
// also remove the surrounding quotes
$sToken =
substr($address,$i+
1,$iEnd -
$i -
1);
if ($sToken) $aTokens[] =
$sToken;
if (!$iEnd ||
$iEnd <
$i) {
$sToken =
substr($address,$i);
$sToken =
substr($address,$i,$iEnd -
$i +
1);
$iEnd =
strpos($address,' ',$i+
1);
if ($sToken) $aTokens[] =
$sToken;
$sPersonal =
$sEmail =
$sComment =
$sGroup =
'';
$aStack =
$aComment =
array();
foreach ($aTokens as $sToken) {
if ($max &&
$max ==
count($aAddress)) {
$aComment[] =
substr($sToken,1,-
1);
$aAddress[] =
array($sGroup,$sEmail);
$aStack =
$aComment =
array();
while (count($aStack) &&
!$sEmail) {
if (!$sPersonal &&
count($aComment)) {
$sComment =
implode(' ',$aComment);
$aAddress[] =
array($sEmail,$sPersonal);
$sPersonal =
$sComment =
$sEmail =
'';
$aStack =
$aComment =
array();
$sGroup =
implode(' ',$aStack); break;
default:
$aStack[] =
$sToken; break;
/* now do the action again for the last address */
while (count($aStack) &&
!$sEmail) {
if (!$sPersonal &&
count($aComment)) {
$sComment =
implode(' ',$aComment);
$aAddress[] =
array($sEmail,$sPersonal);
* Returns the number of unseen messages in this folder.
$read_ary =
sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (UNSEEN)", false, $result, $message);
$regs =
array(false, false);
while (isset
($read_ary[$i])) {
if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
* Returns the number of unseen/total messages in this folder
$read_ary =
sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (MESSAGES UNSEEN RECENT)", false, $result, $message);
$messages =
$unseen =
$recent =
false;
$regs =
array(false,false);
while (isset
($read_ary[$i])) {
if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
return array('MESSAGES' =>
$messages, 'UNSEEN'=>
$unseen, 'RECENT' =>
$recent);
* Saves a message to a given folder -- used for saving sent messages
$tmp =
fgets ($imap_stream, 1024);
fputs ($imap_stream, "\r\n");
$tmp =
fgets ($imap_stream, 1024);
if (preg_match("/(.*)(BAD|NO)(.*)$/", $response, $regs)) {
global $squirrelmail_language, $color;
require_once(SM_PATH .
'functions/display_messages.php');
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Could not append message to") .
" $folder." .
_("Server responded:") .
' ' .
if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
$string .=
_("Solution:") .
' ' .
_("Remove unneccessary messages from your folders. Start with your Trash folder.")
$string =
"<b><font color=\"$color[2]\">\n" .
_("ERROR: Bad or malformed request.") .
_("Server responded:") .
' ' .
$reason .
"</font><br />\n";
if (substr($imap_server, 0, 4) !=
"map:") {
$function =
substr($imap_server, 4);
return $function($username);
* This is an example that gets IMAP servers from yellowpages (NIS).
* you can simple put map:map_yp_alias in your $imap_server_address
* in config.php use your own function instead map_yp_alias to map your
* LDAP whatever way to find the users IMAP server.
$yp = `
ypmatch $username aliases`
;
Documentation generated on Sat, 07 Oct 2006 16:31:39 +0300 by phpDocumentor 1.3.0RC6