6 * Copyright (c) 1999-2003 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
9 * This implements all functions that do general imap functions.
14 require_once(SM_PATH
. 'functions/page_header.php');
15 require_once(SM_PATH
. 'functions/auth.php');
18 global $sqimap_session_id;
19 $sqimap_session_id = 1;
21 /* Sets an unique session id in order to avoid simultanous sessions crash. */
22 function sqimap_session_id($unique_id = false) {
23 global $data_dir, $username, $sqimap_session_id;
25 return( sprintf("A%03d", $sqimap_session_id++
) );
27 return( sprintf("A%03d", $sqimap_session_id++
) . ' UID' );
32 * Both send a command and accept the result from the command.
33 * This is to allow proper session number handling.
35 function sqimap_run_command_list ($imap_stream, $query, $handle_errors, &$response, &$message, $unique_id = false) {
37 $sid = sqimap_session_id($unique_id);
38 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
39 $read = sqimap_read_data_list ($imap_stream, $sid, $handle_errors, $response, $message, $query );
42 global $squirrelmail_language, $color;
43 set_up_language($squirrelmail_language);
44 require_once(SM_PATH
. 'functions/display_messages.php');
45 $string = "<b><font color=$color[2]>\n" .
46 _("ERROR : No available imapstream.") .
48 error_box($string,$color);
54 function sqimap_run_command ($imap_stream, $query, $handle_errors, &$response,
55 &$message, $unique_id = false,$filter=false,
56 $outputstream=false,$no_return=false) {
58 $sid = sqimap_session_id($unique_id);
59 fputs ($imap_stream, $sid . ' ' . $query . "\r\n");
60 $read = sqimap_read_data ($imap_stream, $sid, $handle_errors, $response,
61 $message, $query,$filter,$outputstream,$no_return);
64 global $squirrelmail_language, $color;
65 set_up_language($squirrelmail_language);
66 require_once(SM_PATH
. 'functions/display_messages.php');
67 $string = "<b><font color=$color[2]>\n" .
68 _("ERROR : No available imapstream.") .
70 error_box($string,$color);
78 * custom fgets function. gets a line from IMAP
79 * no matter how big it may be
82 function sqimap_fgets($imap_stream) {
87 while (strpos($results, "\r\n", $offset) === false) {
88 if (!($read = fgets($imap_stream, $buffer))) {
89 /* this happens in case of an error */
90 /* reset $results because it's useless */
94 if ( $results != '' ) {
95 $offset = strlen($results) - 1;
102 function sqimap_fread($imap_stream,$iSize,$filter=false,
103 $outputstream=false, $no_return=false) {
104 if (!$filter ||
!$outputstream) {
105 $iBufferSize = $iSize;
107 $iBufferSize = 32768;
109 $iRet = $iSize - $iBufferSize;
112 while (($i * $iBufferSize) < $iRet) {
113 $sRead = fread($imap_stream,$iBufferSize);
123 if (is_resource($outputstream)) {
124 fwrite($outputstream,$sRead);
125 } else if ($outputstream == 'php://stdout') {
134 if ($results !== false) {
135 $sRead = fread($imap_stream,($iSize - ($i * $iBufferSize)));
140 if (is_resource($outputstream)) {
141 fwrite($outputstream,$sRead);
142 } else if ($outputstream == 'php://stdout') { // FIXME
157 * Reads the output from the IMAP stream. If handle_errors is set to true,
158 * this will also handle all errors that are received. If it is not set,
159 * the errors will be sent back through $response and $message
162 function sqimap_read_data_list ($imap_stream, $tag_uid, $handle_errors,
163 &$response, &$message, $query = '',
164 $filter = false, $outputstream = false, $no_return = false) {
165 global $color, $squirrelmail_language;
167 $tag_uid_a = explode(' ',trim($tag_uid));
168 $tag = $tag_uid_a[0];
169 $resultlist = array();
171 $read = sqimap_fgets($imap_stream);
179 $read = sqimap_fgets($imap_stream);
184 /* get the command */
187 $s = substr($read,$i);
188 if (($j = strpos($s,' ')) ||
($j = strpos($s,"\n"))) {
189 $arg = substr($s,0,$j);
191 $found_tag = substr($read,0,$i-1);
192 if ($arg && $found_tag==$tag) {
201 $message = trim(substr($read,$i+
strlen($arg)));
202 break 3; /* switch switch while */
204 /* this shouldn't happen */
206 $message = trim(substr($read,$i+
strlen($arg)));
207 break 3; /* switch switch while */
209 } elseif($found_tag !== $tag) {
210 /* reset data array because we do not need this reponse */
212 $read = sqimap_fgets($imap_stream);
215 } // end case $tag{0}
219 if (preg_match('/^\*\s\d+\sFETCH/',$read)) {
220 /* check for literal */
221 $s = substr($read,-3);
222 $fetch_data = array();
223 do { /* outer loop, continue until next untagged fetch
225 do { /* innerloop for fetching literals. with this loop
226 we prohibid that literal responses appear in the
227 outer loop so we can trust the untagged and
228 tagged info provided by $read */
229 if ($s === "}\r\n") {
230 $j = strrpos($read,'{');
231 $iLit = substr($read,$j+
1,-3);
232 $fetch_data[] = $read;
233 $sLiteral = sqimap_fread($imap_stream,$iLit,$filter,$outputstream,$no_return);
234 if ($sLiteral === false) { /* error */
235 break 4; /* while while switch while */
237 /* backwards compattibility */
238 $aLiteral = explode("\n", $sLiteral);
239 /* release not neaded data */
241 foreach ($aLiteral as $line) {
242 $fetch_data[] = $line ."\n";
244 /* release not neaded data */
246 /* next fgets belongs to this fetch because
247 we just got the exact literalsize and there
248 must follow data to complete the response */
249 $read = sqimap_fgets($imap_stream);
250 if ($read === false) { /* error */
251 break 4; /* while while switch while */
253 $fetch_data[] = $read;
255 $fetch_data[] = $read;
257 /* retrieve next line and check in the while
258 statements if it belongs to this fetch response */
259 $read = sqimap_fgets($imap_stream);
260 if ($read === false) { /* error */
261 break 4; /* while while switch while */
263 /* check for next untagged reponse and break */
264 if ($read{0} == '*') break 2;
265 $s = substr($read,-3);
266 } while ($s === "}\r\n");
267 $s = substr($read,-3);
268 } while ($read{0} !== '*' &&
269 substr($read,0,strlen($tag)) !== $tag);
270 $resultlist[] = $fetch_data;
271 /* release not neaded data */
274 $s = substr($read,-3);
276 if ($s === "}\r\n") {
277 $j = strrpos($read,'{');
278 $iLit = substr($read,$j+
1,-3);
280 $sLiteral = fread($imap_stream,$iLit);
281 if ($sLiteral === false) { /* error */
283 break 3; /* while switch while */
286 $fetch_data[] = sqimap_fgets($imap_stream);
290 $read = sqimap_fgets($imap_stream);
291 if ($read === false) {
292 break 3; /* while switch while */
293 } else if ($read{0} == '*') {
296 $s = substr($read,-3);
297 } while ($s === "}\r\n");
305 /* error processing in case $read is false */
306 if ($read === false) {
308 set_up_language($squirrelmail_language);
309 require_once(SM_PATH
. 'functions/display_messages.php');
310 $string = "<b><font color=$color[2]>\n" .
311 _("ERROR : Connection dropped by imap-server.") .
314 htmlspecialchars($query) . '<br>' . "</font><br>\n";
315 error_box($string,$color);
319 /* Set $resultlist array */
321 $resultlist[] = $data;
323 elseif (empty($resultlist)) {
324 $resultlist[] = array();
327 /* Return result or handle errors */
328 if ($handle_errors == false) {
329 return( $resultlist );
337 /* ignore this error from M$ exchange, it is not fatal (aka bug) */
338 if (strstr($message, 'command resulted in') === false) {
339 set_up_language($squirrelmail_language);
340 require_once(SM_PATH
. 'functions/display_messages.php');
341 $string = "<b><font color=$color[2]>\n" .
342 _("ERROR : Could not complete request.") .
345 htmlspecialchars($query) . '<br>' .
346 _("Reason Given: ") .
347 htmlspecialchars($message) . "</font><br>\n";
348 error_box($string,$color);
349 echo '</body></html>';
354 set_up_language($squirrelmail_language);
355 require_once(SM_PATH
. 'functions/display_messages.php');
356 $string = "<b><font color=$color[2]>\n" .
357 _("ERROR : Bad or malformed request.") .
360 htmlspecialchars($query) . '<br>' .
361 _("Server responded: ") .
362 htmlspecialchars($message) . "</font><br>\n";
363 error_box($string,$color);
364 echo '</body></html>';
367 set_up_language($squirrelmail_language);
368 require_once(SM_PATH
. 'functions/display_messages.php');
369 $string = "<b><font color=$color[2]>\n" .
370 _("ERROR : Imap server closed the connection.") .
373 htmlspecialchars($query) . '<br>' .
374 _("Server responded: ") .
375 htmlspecialchars($message) . "</font><br>\n";
376 error_box($string,$color);
377 echo '</body></html>';
380 set_up_language($squirrelmail_language);
381 require_once(SM_PATH
. 'functions/display_messages.php');
382 $string = "<b><font color=$color[2]>\n" .
383 _("ERROR : Unknown imap response.") .
386 htmlspecialchars($query) . '<br>' .
387 _("Server responded: ") .
388 htmlspecialchars($message) . "</font><br>\n";
389 error_box($string,$color);
390 /* the error is displayed but because we don't know the reponse we
391 return the result anyway */
397 function sqimap_read_data ($imap_stream, $tag_uid, $handle_errors,
398 &$response, &$message, $query = '',
399 $filter=false,$outputstream=false,$no_return=false) {
400 $res = sqimap_read_data_list($imap_stream, $tag_uid, $handle_errors,
401 $response, $message, $query,$filter,$outputstream,$no_return);
402 /* sqimap_read_data should be called for one response
403 but since it just calls sqimap_read_data_list which
404 handles multiple responses we need to check for that
405 and merge the $res array IF they are seperated and
406 IF it was a FETCH response. */
408 // if (isset($res[1]) && is_array($res[1]) && isset($res[1][0])
409 // && preg_match('/^\* \d+ FETCH/', $res[1][0])) {
410 // $result = array();
411 // foreach($res as $index=>$value) {
412 // $result = array_merge($result, $res["$index"]);
415 if (isset($result)) {
425 * Logs the user into the imap server. If $hide is set, no error messages
426 * will be displayed. This function returns the imap connection handle.
428 function sqimap_login ($username, $password, $imap_server_address, $imap_port, $hide) {
429 global $color, $squirrelmail_language, $onetimepad, $use_imap_tls, $imap_auth_mech;
431 if (!isset($onetimepad) ||
empty($onetimepad)) {
432 sqgetglobalvar('onetimepad' , $onetimepad , SQ_SESSION
);
434 $imap_server_address = sqimap_get_user_server($imap_server_address, $username);
435 $host=$imap_server_address;
437 if (($use_imap_tls == true) and (check_php_version(4,3)) and (extension_loaded('openssl'))) {
438 /* Use TLS by prefixing "tls://" to the hostname */
439 $imap_server_address = 'tls://' . $imap_server_address;
442 $imap_stream = fsockopen ( $imap_server_address, $imap_port, $error_number, $error_string, 15);
444 /* Do some error correction */
447 set_up_language($squirrelmail_language, true);
448 require_once(SM_PATH
. 'functions/display_messages.php');
449 $string = sprintf (_("Error connecting to IMAP server: %s.") .
450 "<br>\r\n", $imap_server_address) .
451 "$error_number : $error_string<br>\r\n";
452 logout_error($string,$color);
457 $server_info = fgets ($imap_stream, 1024);
459 /* Decrypt the password */
460 $password = OneTimePadDecrypt($password, $onetimepad);
462 if (($imap_auth_mech == 'cram-md5') OR ($imap_auth_mech == 'digest-md5')) {
463 // We're using some sort of authentication OTHER than plain or login
464 $tag=sqimap_session_id(false);
465 if ($imap_auth_mech == 'digest-md5') {
466 $query = $tag . " AUTHENTICATE DIGEST-MD5\r\n";
467 } elseif ($imap_auth_mech == 'cram-md5') {
468 $query = $tag . " AUTHENTICATE CRAM-MD5\r\n";
470 fputs($imap_stream,$query);
471 $answer=sqimap_fgets($imap_stream);
472 // Trim the "+ " off the front
473 $response=explode(" ",$answer,3);
474 if ($response[0] == '+') {
475 // Got a challenge back
476 $challenge=$response[1];
477 if ($imap_auth_mech == 'digest-md5') {
478 $reply = digest_md5_response($username,$password,$challenge,'imap',$host);
479 } elseif ($imap_auth_mech == 'cram-md5') {
480 $reply = cram_md5_response($username,$password,$challenge);
482 fputs($imap_stream,$reply);
483 $read=sqimap_fgets($imap_stream);
484 if ($imap_auth_mech == 'digest-md5') {
485 // DIGEST-MD5 has an extra step..
486 if (substr($read,0,1) == '+') { // OK so far..
487 fputs($imap_stream,"\r\n");
488 $read=sqimap_fgets($imap_stream);
491 $results=explode(" ",$read,3);
492 $response=$results[1];
493 $message=$results[2];
495 // Fake the response, so the error trap at the bottom will work
497 $message='IMAP server does not appear to support the authentication method selected.';
498 $message .= ' Please contact your system administrator.';
500 } elseif ($imap_auth_mech == 'login') {
501 // Original IMAP login code
502 $query = 'LOGIN "' . quoteimap($username) . '" "' . quoteimap($password) . '"';
503 $read = sqimap_run_command ($imap_stream, $query, false, $response, $message);
504 } elseif ($imap_auth_mech == 'plain') {
505 /* Replace this with SASL PLAIN if it ever gets implemented */
507 $message='SquirrelMail does not support SASL PLAIN yet. Rerun conf.pl and use login instead.';
510 $message="Internal SquirrelMail error - unknown IMAP authentication method chosen. Please contact the developers.";
513 /* If the connection was not successful, lets see why */
514 if ($response != 'OK') {
516 if ($response != 'NO') {
517 /* "BAD" and anything else gets reported here. */
518 $message = htmlspecialchars($message);
519 set_up_language($squirrelmail_language, true);
520 require_once(SM_PATH
. 'functions/display_messages.php');
521 if ($response == 'BAD') {
522 $string = sprintf (_("Bad request: %s")."<br>\r\n", $message);
524 $string = sprintf (_("Unknown error: %s") . "<br>\n", $message);
526 if (isset($read) && is_array($read)) {
527 $string .= '<br>' . _("Read data:") . "<br>\n";
528 foreach ($read as $line) {
529 $string .= htmlspecialchars($line) . "<br>\n";
532 error_box($string,$color);
536 * If the user does not log in with the correct
537 * username and password it is not possible to get the
538 * correct locale from the user's preferences.
539 * Therefore, apply the same hack as on the login
542 * $squirrelmail_language is set by a cookie when
543 * the user selects language and logs out
546 set_up_language($squirrelmail_language, true);
547 include_once(SM_PATH
. 'functions/display_messages.php' );
549 logout_error( _("Unknown user or password incorrect.") );
559 /* Simply logs out the IMAP session */
560 function sqimap_logout ($imap_stream) {
561 /* Logout is not valid until the server returns 'BYE'
562 * If we don't have an imap_ stream we're already logged out */
563 if(isset($imap_stream) && $imap_stream)
564 sqimap_run_command($imap_stream, 'LOGOUT', false, $response, $message);
567 function sqimap_capability($imap_stream, $capability='') {
568 global $sqimap_capabilities;
569 if (!is_array($sqimap_capabilities)) {
570 $read = sqimap_run_command($imap_stream, 'CAPABILITY', true, $a, $b);
572 $c = explode(' ', $read[0]);
573 for ($i=2; $i < count($c); $i++
) {
574 $cap_list = explode('=', $c[$i]);
575 if (isset($cap_list[1])) {
576 $sqimap_capabilities[$cap_list[0]] = $cap_list[1];
578 $sqimap_capabilities[$cap_list[0]] = TRUE;
583 if (isset($sqimap_capabilities[$capability])) {
584 return $sqimap_capabilities[$capability];
589 return $sqimap_capabilities;
592 /* Returns the delimeter between mailboxes: INBOX/Test, or INBOX.Test */
593 function sqimap_get_delimiter ($imap_stream = false) {
594 global $sqimap_delimiter, $optional_delimiter;
596 /* Use configured delimiter if set */
597 if((!empty($optional_delimiter)) && $optional_delimiter != 'detect') {
598 return $optional_delimiter;
601 /* Do some caching here */
602 if (!$sqimap_delimiter) {
603 if (sqimap_capability($imap_stream, 'NAMESPACE')) {
605 * According to something that I can't find, this is supposed to work on all systems
606 * OS: This won't work in Courier IMAP.
607 * OS: According to rfc2342 response from NAMESPACE command is:
608 * OS: * NAMESPACE (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
609 * OS: We want to lookup all personal NAMESPACES...
611 $read = sqimap_run_command($imap_stream, 'NAMESPACE', true, $a, $b);
612 if (eregi('\\* NAMESPACE +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL) +(\\( *\\(.+\\) *\\)|NIL)', $read[0], $data)) {
613 if (eregi('^\\( *\\((.*)\\) *\\)', $data[1], $data2)) {
616 $pna = explode(')(', $pn);
617 while (list($k, $v) = each($pna)) {
618 $lst = explode('"', $v);
619 if (isset($lst[3])) {
620 $pn[$lst[1]] = $lst[3];
626 $sqimap_delimiter = $pn[0];
628 fputs ($imap_stream, ". LIST \"INBOX\" \"\"\r\n");
629 $read = sqimap_read_data($imap_stream, '.', true, $a, $b);
630 $quote_position = strpos ($read[0], '"');
631 $sqimap_delimiter = substr ($read[0], $quote_position+
1, 1);
634 return $sqimap_delimiter;
638 /* Gets the number of messages in the current mailbox. */
639 function sqimap_get_num_messages ($imap_stream, $mailbox) {
640 $read_ary = sqimap_run_command ($imap_stream, "EXAMINE \"$mailbox\"", false, $result, $message);
641 for ($i = 0; $i < count($read_ary); $i++
) {
642 if (ereg("[^ ]+ +([^ ]+) +EXISTS", $read_ary[$i], $regs)) {
646 return false; //"BUG! Couldn't get number of messages in $mailbox!";
650 function parseAddress($address, $max=0) {
653 $iCnt = strlen($address);
654 $aSpecials = array('(' ,'<' ,',' ,';' ,':');
655 $aReplace = array(' (',' <',' ,',' ;',' :');
656 $address = str_replace($aSpecials,$aReplace,$address);
659 $cChar = $address{$i};
663 $iEnd = strpos($address,'>',$i+
1);
665 $sToken = substr($address,$i);
668 $sToken = substr($address,$i,$iEnd - $i +
1);
671 $sToken = str_replace($aReplace, $aSpecials,$sToken);
672 $aTokens[] = $sToken;
675 $iEnd = strpos($address,$cChar,$i+
1);
677 $sToken = substr($address,$i);
680 // also remove the surrounding quotes
681 $sToken = substr($address,$i+
1,$iEnd - $i -1);
684 $sToken = str_replace($aReplace, $aSpecials,$sToken);
685 if ($sToken) $aTokens[] = $sToken;
688 $iEnd = strpos($address,')',$i);
690 $sToken = substr($address,$i);
693 $sToken = substr($address,$i,$iEnd - $i +
1);
696 $sToken = str_replace($aReplace, $aSpecials,$sToken);
697 $aTokens[] = $sToken;
706 $iEnd = strpos($address,' ',$i+
1);
708 $sToken = trim(substr($address,$i,$iEnd - $i));
711 $sToken = trim(substr($address,$i));
714 if ($sToken) $aTokens[] = $sToken;
718 $sPersonal = $sEmail = $sComment = $sGroup = '';
719 $aStack = $aComment = array();
720 foreach ($aTokens as $sToken) {
721 if ($max && $max == count($aAddress)) {
733 $aComment[] = substr($sToken,1,-1);
737 $sEmail = trim(implode(' ',$aStack));
738 $aAddress[] = array($sGroup,$sEmail);
739 $aStack = $aComment = array();
745 while (count($aStack) && !$sEmail) {
746 $sEmail = trim(array_pop($aStack));
749 if (count($aStack)) {
750 $sPersonal = trim(implode('',$aStack));
754 if (!$sPersonal && count($aComment)) {
755 $sComment = implode(' ',$aComment);
756 $sPersonal .= $sComment;
758 $aAddress[] = array($sEmail,$sPersonal);
759 $sPersonal = $sComment = $sEmail = '';
760 $aStack = $aComment = array();
763 $sGroup = implode(' ',$aStack); break;
767 $sEmail = trim(substr($sToken,1,-1));
772 default: $aStack[] = $sToken; break;
775 /* now do the action again for the last address */
777 while (count($aStack) && !$sEmail) {
778 $sEmail = trim(array_pop($aStack));
781 if (count($aStack)) {
782 $sPersonal = trim(implode('',$aStack));
786 if (!$sPersonal && count($aComment)) {
787 $sComment = implode(' ',$aComment);
788 $sPersonal .= $sComment;
790 $aAddress[] = array($sEmail,$sPersonal);
797 * Returns the number of unseen messages in this folder
799 function sqimap_unseen_messages ($imap_stream, $mailbox) {
800 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (UNSEEN)", false, $result, $message);
802 $regs = array(false, false);
803 while (isset($read_ary[$i])) {
804 if (ereg("UNSEEN ([0-9]+)", $read_ary[$i], $regs)) {
813 * Returns the number of unseen/total messages in this folder
815 function sqimap_status_messages ($imap_stream, $mailbox) {
816 $read_ary = sqimap_run_command ($imap_stream, "STATUS \"$mailbox\" (MESSAGES UNSEEN RECENT)", false, $result, $message);
818 $messages = $unseen = $recent = false;
819 $regs = array(false,false);
820 while (isset($read_ary[$i])) {
821 if (preg_match('/UNSEEN\s+([0-9]+)/i', $read_ary[$i], $regs)) {
824 if (preg_match('/MESSAGES\s+([0-9]+)/i', $read_ary[$i], $regs)) {
825 $messages = $regs[1];
827 if (preg_match('/RECENT\s+([0-9]+)/i', $read_ary[$i], $regs)) {
832 return array('MESSAGES' => $messages, 'UNSEEN'=>$unseen, 'RECENT' => $recent);
837 * Saves a message to a given folder -- used for saving sent messages
839 function sqimap_append ($imap_stream, $sent_folder, $length) {
840 fputs ($imap_stream, sqimap_session_id() . " APPEND \"$sent_folder\" (\\Seen) \{$length}\r\n");
841 $tmp = fgets ($imap_stream, 1024);
844 function sqimap_append_done ($imap_stream, $folder='') {
845 global $squirrelmail_language, $color;
846 fputs ($imap_stream, "\r\n");
847 $tmp = fgets ($imap_stream, 1024);
848 if (preg_match("/(.*)(BAD|NO)(.*)$/", $tmp, $regs)) {
849 set_up_language($squirrelmail_language);
850 require_once(SM_PATH
. 'functions/display_messages.php');
852 if ($regs[2] == 'NO') {
853 $string = "<b><font color=$color[2]>\n" .
854 _("ERROR : Could not append message to") ." $folder." .
856 _("Server responded: ") .
858 if (preg_match("/(.*)(quota)(.*)$/i", $reason, $regs)) {
859 $string .= _("Solution: ") .
860 _("Remove unneccessary messages from your folder and start with your Trash folder.")
863 $string .= "</font>\n";
864 error_box($string,$color);
866 $string = "<b><font color=$color[2]>\n" .
867 _("ERROR : Bad or malformed request.") .
869 _("Server responded: ") .
870 $tmp . "</font><br>\n";
871 error_box($string,$color);
877 function sqimap_get_user_server ($imap_server, $username) {
878 if (substr($imap_server, 0, 4) != "map:") {
881 $function = substr($imap_server, 4);
882 return $function($username);
885 /* This is an example that gets imapservers from yellowpages (NIS).
886 * you can simple put map:map_yp_alias in your $imap_server_address
887 * in config.php use your own function instead map_yp_alias to map your
888 * LDAP whatever way to find the users imapserver. */
890 function map_yp_alias($username) {
891 $yp = `ypmatch
$username aliases`
;
892 return chop(substr($yp, strlen($username)+
1));