X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=class%2Fdeliver%2FDeliver.class.php;h=e9038fdd54f99fe96d2b5b0538f4082263b3b004;hp=7789740b6270832d889f8e7215114c4d78811a5b;hb=10adeb76533751d8f9dd78c5b27e8e0828e1d5b1;hpb=72b004e6e97f7b02f46b97d5ebca21182720acb9 diff --git a/class/deliver/Deliver.class.php b/class/deliver/Deliver.class.php index 7789740b..e9038fdd 100644 --- a/class/deliver/Deliver.class.php +++ b/class/deliver/Deliver.class.php @@ -1,20 +1,18 @@ rfc822_header; + function mail(&$message, $stream=false, $reply_id=0, $reply_ent_id=0, + $extra=NULL) { + + $rfc822_header = &$message->rfc822_header; + if (count($message->entities)) { $boundary = $this->mimeBoundary(); $rfc822_header->content_type->properties['boundary']='"'.$boundary.'"'; @@ -47,16 +73,80 @@ class Deliver { $boundary=''; } $raw_length = 0; + + + // calculate reply header if needed + // + if ($reply_id) { + global $imapConnection, $username, $imapServerAddress, + $imapPort, $mailbox; + + if (!is_resource($imapConnection)) + $imapConnection = sqimap_login($username, FALSE, + $imapServerAddress, $imapPort, 0); + + sqimap_mailbox_select($imapConnection, $mailbox); + $reply_message = sqimap_get_message($imapConnection, $reply_id, $mailbox); + + if ($reply_ent_id) { + /* redefine the messsage in case of message/rfc822 */ + $reply_message = $message->getEntity($reply_ent_id); + /* message is an entity which contains the envelope and type0=message + * and type1=rfc822. The actual entities are childs from + * $reply_message->entities[0]. That's where the encoding and is located + */ + + $orig_header = $reply_message->rfc822_header; /* here is the envelope located */ + + } else { + $orig_header = $reply_message->rfc822_header; + } + $message->reply_rfc822_header = $orig_header; + } + + $reply_rfc822_header = (isset($message->reply_rfc822_header) ? $message->reply_rfc822_header : ''); $header = $this->prepareRFC822_Header($rfc822_header, $reply_rfc822_header, $raw_length); + $this->send_mail($message, $header, $boundary, $stream, $raw_length, $extra); + + return $raw_length; + } + + /** + * function send_mail - send the message parts to the IMAP stream + * + * @param Message $message Message object to send + * @param string $header Headers ready to send + * @param string $boundary Message parts boundary + * @param resource $stream Handle to the SMTP stream + * (when FALSE, nothing will be + * written to the stream; this can + * be used to determine the actual + * number of bytes that will be + * written to the stream) + * @param int &$raw_length The number of bytes written (or that + * would have been written) to the + * output stream - NOTE that this is + * passed by reference + * @param mixed $extra Any implementation-specific variables + * can be passed in here and used in + * an overloaded version of this method + * if needed. + * + * @return void + * + */ + function send_mail($message, $header, $boundary, $stream=false, + &$raw_length, $extra=NULL) { + + if ($stream) { $this->preWriteToStream($header); $this->writeToStream($stream, $header); } $this->writeBody($message, $stream, $raw_length, $boundary); - return $raw_length; } /** @@ -67,19 +157,33 @@ class Deliver { * * @param Message $message Message object to transform * @param resource $stream SMTP output stream + * (when FALSE, nothing will be + * written to the stream; this can + * be used to determine the actual + * number of bytes that will be + * written to the stream) * @param integer &$length_raw raw length of the message (part) * as returned by mail fn * @param string $boundary custom boundary to call, usually for subparts * - * @returns void + * @return void */ function writeBody($message, $stream, &$length_raw, $boundary='') { // calculate boundary in case of multidimensional mime structures if ($boundary && $message->entity_id && count($message->entities)) { if (strpos($boundary,'_part_')) { $boundary = substr($boundary,0,strpos($boundary,'_part_')); + + // the next four lines use strrev to reverse any nested boundaries + // because RFC 2046 (5.1.1) says that if a line starts with the outer + // boundary string (doesn't matter what the line ends with), that + // can be considered a match for the outer boundary; thus the nested + // boundary needs to be unique from the outer one + // + } else if (strpos($boundary,'_trap_')) { + $boundary = substr(strrev($boundary),0,strpos(strrev($boundary),'_part_')); } - $boundary_new = $boundary . '_part_'.$message->entity_id; + $boundary_new = strrev($boundary . '_part_'.$message->entity_id); } else { $boundary_new = $boundary; } @@ -116,10 +220,15 @@ class Deliver { * * @param Message $message Message object to transform * @param resource $stream SMTP output stream + * (when FALSE, nothing will be + * written to the stream; this can + * be used to determine the actual + * number of bytes that will be + * written to the stream) * @param integer &$length length of the message part * as returned by mail fn * - * @returns void + * @return void */ function writeBodyPart($message, $stream, &$length) { if ($message->mime_header) { @@ -135,6 +244,8 @@ class Deliver { case 'message': if ($message->body_part) { $body_part = $message->body_part; + // remove NUL characters + $body_part = str_replace("\0",'',$body_part); $length += $this->clean_crlf($body_part); if ($stream) { $this->preWriteToStream($body_part); @@ -142,9 +253,13 @@ class Deliver { } $last = $body_part; } elseif ($message->att_local_name) { + global $username, $attachment_dir; + $hashed_attachment_dir = getHashedDir($username, $attachment_dir); $filename = $message->att_local_name; - $file = fopen ($filename, 'rb'); + $file = fopen ($hashed_attachment_dir . '/' . $filename, 'rb'); while ($body_part = fgets($file, 4096)) { + // remove NUL characters + $body_part = str_replace("\0",'',$body_part); $length += $this->clean_crlf($body_part); if ($stream) { $this->preWriteToStream($body_part); @@ -158,16 +273,24 @@ class Deliver { default: if ($message->body_part) { $body_part = $message->body_part; + // remove NUL characters + $body_part = str_replace("\0",'',$body_part); $length += $this->clean_crlf($body_part); if ($stream) { $this->writeToStream($stream, $body_part); } } elseif ($message->att_local_name) { + global $username, $attachment_dir; + $hashed_attachment_dir = getHashedDir($username, $attachment_dir); $filename = $message->att_local_name; - $file = fopen ($filename, 'rb'); - $encoded = ''; + $file = fopen ($hashed_attachment_dir . '/' . $filename, 'rb'); while ($tmp = fread($file, 570)) { - $body_part = chunk_split(base64_encode($tmp)); + $body_part = chunk_split(base64_encode($tmp)); + // Up to 4.3.10 chunk_split always appends a newline, + // while in 4.3.11 it doesn't if the string to split + // is shorter than the chunk length. + if( substr($body_part, -1 , 1 ) != "\n" ) + $body_part .= "\n"; $length += $this->clean_crlf($body_part); if ($stream) { $this->writeToStream($stream, $body_part); @@ -198,7 +321,7 @@ class Deliver { * * @param string &$s string to clean linefeeds on * - * @returns void + * @return void */ function clean_crlf(&$s) { $s = str_replace("\r\n", "\n", $s); @@ -215,7 +338,7 @@ class Deliver { * * @param string &$s string to clean linefeeds on * - * @returns void + * @return void */ function strip_crlf(&$s) { $s = str_replace("\r\n ", '', $s); @@ -231,7 +354,7 @@ class Deliver { * * @param string &$s string to operate on * - * @returns void + * @return void */ function preWriteToStream(&$s) { } @@ -242,7 +365,7 @@ class Deliver { * @param resource $stream SMTP output stream * @param string $data string with data to send to the SMTP stream * - * @returns void + * @return void */ function writeToStream($stream, $data) { fputs($stream, $data); @@ -260,7 +383,7 @@ class Deliver { * @param string $pass password to log into the SMTP server with * @param integer $length * - * @returns handle $stream file handle resource to SMTP stream + * @return handle $stream file handle resource to SMTP stream */ function initStream($message, $length=0, $host='', $port='', $user='', $pass='') { return $stream; @@ -283,7 +406,7 @@ class Deliver { * @param Message $message Message object to act on * @param string $boundary mime boundary from fn MimeBoundary * - * @returns string $header properly formatted mime header + * @return string $header properly formatted mime header */ function prepareMIME_Header($message, $boundary) { $mime_header = $message->mime_header; @@ -293,7 +416,7 @@ class Deliver { $contenttype = 'Content-Type: '. $mime_header->type0 .'/'. $mime_header->type1; if (count($message->entities)) { - $contenttype .= ";\r\n " . 'boundary="'.$boundary.'"'; + $contenttype .= ';' . 'boundary="'.$boundary.'"'; } if (isset($mime_header->parameters['name'])) { $contenttype .= '; name="'. @@ -307,20 +430,19 @@ class Deliver { $header[] = $contenttype . $rn; if ($mime_header->description) { - $header[] .= 'Content-Description: ' . $mime_header->description . $rn; + $header[] = 'Content-Description: ' . $mime_header->description . $rn; } if ($mime_header->encoding) { - $encoding = $mime_header->encoding; - $header[] .= 'Content-Transfer-Encoding: ' . $mime_header->encoding . $rn; + $header[] = 'Content-Transfer-Encoding: ' . $mime_header->encoding . $rn; } else { if ($mime_header->type0 == 'text' || $mime_header->type0 == 'message') { - $header[] .= 'Content-Transfer-Encoding: 8bit' . $rn; + $header[] = 'Content-Transfer-Encoding: 8bit' . $rn; } else { - $header[] .= 'Content-Transfer-Encoding: base64' . $rn; + $header[] = 'Content-Transfer-Encoding: base64' . $rn; } } if ($mime_header->id) { - $header[] .= 'Content-ID: ' . $mime_header->id . $rn; + $header[] = 'Content-ID: ' . $mime_header->id . $rn; } if ($mime_header->disposition) { $disposition = $mime_header->disposition; @@ -332,10 +454,10 @@ class Deliver { $header[] = $contentdisp . $rn; } if ($mime_header->md5) { - $header[] .= 'Content-MD5: ' . $mime_header->md5 . $rn; + $header[] = 'Content-MD5: ' . $mime_header->md5 . $rn; } if ($mime_header->language) { - $header[] .= 'Content-Language: ' . $mime_header->language . $rn; + $header[] = 'Content-Language: ' . $mime_header->language . $rn; } $cnt = count($header); @@ -359,13 +481,16 @@ class Deliver { * @param Rfc822Header $reply_rfc822_header * @param integer &$raw_length length of the message * - * @returns + * @return string $header */ - function prepareRFC822_Header($rfc822_header, $reply_rfc822_header, &$raw_length) { - global $domain, $version, $username; + function prepareRFC822_Header(&$rfc822_header, $reply_rfc822_header, &$raw_length) { + global $domain, $username, $encode_header_key, + $edit_identity, $hide_auth_header; - /* if server var SERVER_NAME not available, use $domain */ - if(!sqGetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER)) { + /* if server var SERVER_NAME not available, or contains + ":" (e.g. IPv6) which is illegal in a Message-ID, use $domain */ + if(!sqGetGlobalVar('SERVER_NAME', $SERVER_NAME, SQ_SERVER) || + strpos($SERVER_NAME,':') !== FALSE) { $SERVER_NAME = $domain; } @@ -378,10 +503,22 @@ class Deliver { $rn = "\r\n"; /* This creates an RFC 822 date */ - $date = date('D, j M Y H:i:s ', mktime()) . $this->timezone(); + $date = date('D, j M Y H:i:s ', time()) . $this->timezone(); + /* Create a message-id */ - $message_id = '<' . $REMOTE_PORT . '.' . $REMOTE_ADDR . '.'; - $message_id .= time() . '.squirrel@' . $REMOTE_ADDR .'>'; + $message_id = 'MESSAGE ID GENERATION ERROR! PLEASE CONTACT SQUIRRELMAIL DEVELOPERS'; + if (empty($rfc822_header->message_id)) { + $message_id = '<'; + /* user-specifc data to decrease collision chance */ + $seed_data = $username . '.'; + $seed_data .= (!empty($REMOTE_PORT) ? $REMOTE_PORT . '.' : ''); + $seed_data .= (!empty($REMOTE_ADDR) ? $REMOTE_ADDR . '.' : ''); + /* add the current time in milliseconds and randomness */ + $seed_data .= uniqid(mt_rand(),true); + /* put it through one-way hash and add it to the ID */ + $message_id .= md5($seed_data) . '.squirrel@' . $SERVER_NAME .'>'; + } + /* Make an RFC822 Received: line */ if (isset($REMOTE_HOST)) { $received_from = "$REMOTE_HOST ([$REMOTE_ADDR])"; @@ -395,31 +532,86 @@ class Deliver { $received_from .= " (proxying for $HTTP_X_FORWARDED_FOR)"; } $header = array(); - $header[] = "Received: from $received_from" . $rn; - $header[] = " (SquirrelMail authenticated user $username);" . $rn; - $header[] = " by $SERVER_NAME with HTTP;" . $rn; - $header[] = " $date" . $rn; + + /** + * SquirrelMail header + * + * This Received: header provides information that allows to track + * user and machine that was used to send email. Don't remove it + * unless you understand all possible forging issues or your + * webmail installation does not prevent changes in user's email address. + * See SquirrelMail bug tracker #847107 for more details about it. + * + * Add $hide_squirrelmail_header as a candidate for config_local.php + * to allow completely hiding SquirrelMail participation in message + * processing; This is dangerous, especially if users can modify their + * account information, as it makes mapping a sent message back to the + * original sender almost impossible. + */ + $show_sm_header = ( defined('hide_squirrelmail_header') ? ! hide_squirrelmail_header : 1 ); + + // FIXME: The following headers may generate slightly differently between the message sent to the destination and that stored in the Sent folder because this code will be called before both actions. This is not necessarily a big problem, but other headers such as Message-ID and Date are preserved between both actions + if ( $show_sm_header ) { + if (isset($encode_header_key) && + trim($encode_header_key)!='') { + // use encoded headers, if encryption key is set and not empty + $header[] = 'X-Squirrel-UserHash: '.OneTimePadEncrypt($username,base64_encode($encode_header_key)).$rn; + $header[] = 'X-Squirrel-FromHash: '.OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key)).$rn; + if (isset($HTTP_X_FORWARDED_FOR)) + $header[] = 'X-Squirrel-ProxyHash:'.OneTimePadEncrypt($this->ip2hex($HTTP_X_FORWARDED_FOR),base64_encode($encode_header_key)).$rn; + } else { + // use default received headers + $header[] = "Received: from $received_from" . $rn; + if ($edit_identity || ! isset($hide_auth_header) || ! $hide_auth_header) + $header[] = " (SquirrelMail authenticated user $username)" . $rn; + $header[] = " by $SERVER_NAME with HTTP;" . $rn; + $header[] = " $date" . $rn; + } + } + /* Insert the rest of the header fields */ - $header[] = 'Message-ID: '. $message_id . $rn; - if ($reply_rfc822_header->message_id) { + + if (!empty($rfc822_header->message_id)) { + $header[] = 'Message-ID: '. $rfc822_header->message_id . $rn; + } else { + $header[] = 'Message-ID: '. $message_id . $rn; + $rfc822_header->message_id = $message_id; + } + + if (is_object($reply_rfc822_header) && + isset($reply_rfc822_header->message_id) && + $reply_rfc822_header->message_id) { $rep_message_id = $reply_rfc822_header->message_id; - // $this->strip_crlf($message_id); $header[] = 'In-Reply-To: '.$rep_message_id . $rn; + $rfc822_header->in_reply_to = $rep_message_id; $references = $this->calculate_references($reply_rfc822_header); $header[] = 'References: '.$references . $rn; + $rfc822_header->references = $references; + } + + if (!empty($rfc822_header->date) && $rfc822_header->date != -1) { + $header[] = 'Date: '. $rfc822_header->date . $rn; + } else { + $header[] = "Date: $date" . $rn; + $rfc822_header->date = $date; } - $header[] = "Date: $date" . $rn; + $header[] = 'Subject: '.encodeHeader($rfc822_header->subject) . $rn; - $header[] = 'From: '. $rfc822_header->getAddr_s('from',',',true) . $rn; - /* RFC2822 if from contains more then 1 address */ + $header[] = 'From: '. $rfc822_header->getAddr_s('from',",$rn ",true) . $rn; + + // folding address list [From|To|Cc|Bcc] happens by using ",$rn" + // as delimiter + // Do not use foldLine for that. + + // RFC2822 if from contains more then 1 address if (count($rfc822_header->from) > 1) { $header[] = 'Sender: '. $rfc822_header->getAddr_s('sender',',',true) . $rn; } if (count($rfc822_header->to)) { - $header[] = 'To: '. $rfc822_header->getAddr_s('to',',',true) . $rn; + $header[] = 'To: '. $rfc822_header->getAddr_s('to',",$rn ",true) . $rn; } if (count($rfc822_header->cc)) { - $header[] = 'Cc: '. $rfc822_header->getAddr_s('cc',',',true) . $rn; + $header[] = 'Cc: '. $rfc822_header->getAddr_s('cc',",$rn ",true) . $rn; } if (count($rfc822_header->reply_to)) { $header[] = 'Reply-To: '. $rfc822_header->getAddr_s('reply_to',',',true) . $rn; @@ -427,18 +619,15 @@ class Deliver { /* Sendmail should return true. Default = false */ $bcc = $this->getBcc(); if (count($rfc822_header->bcc)) { - $s = 'Bcc: '. $rfc822_header->getAddr_s('bcc',',',true) . $rn; + $s = 'Bcc: '. $rfc822_header->getAddr_s('bcc',",$rn ",true) . $rn; if (!$bcc) { - $s = $this->foldLine($s, 78, str_pad('',4)); $raw_length += strlen($s); } else { $header[] = $s; } } /* Identify SquirrelMail */ - $header[] = 'User-Agent: SquirrelMail/' . $version . $rn; - // Spamassassin complains about no X-Mailer in combination with X-Priority - $header[] = 'X-Mailer: SquirrelMail/' . $version . $rn; + $header[] = 'User-Agent: SquirrelMail/' . SM_VERSION . $rn; /* Do the MIME-stuff */ $header[] = 'MIME-Version: 1.0' . $rn; $contenttype = 'Content-Type: '. $rfc822_header->content_type->type0 .'/'. @@ -452,9 +641,9 @@ class Deliver { } $header[] = $contenttype . $rn; if ($encoding = $rfc822_header->encoding) { - $header[] .= 'Content-Transfer-Encoding: ' . $encoding . $rn; + $header[] = 'Content-Transfer-Encoding: ' . $encoding . $rn; } - if ($rfc822_header->dnt) { + if (isset($rfc822_header->dnt) && $rfc822_header->dnt) { $dnt = $rfc822_header->getAddr_s('dnt'); /* Pegasus Mail */ $header[] = 'X-Confirm-Reading-To: '.$dnt. $rn; @@ -462,13 +651,14 @@ class Deliver { $header[] = 'Disposition-Notification-To: '.$dnt. $rn; } if ($rfc822_header->priority) { - $prio = $rfc822_header->priority; - $header[] = 'X-Priority: '. $prio. $rn; - switch($prio) + switch($rfc822_header->priority) { - case 1: $header[] = 'Importance: High'. $rn; break; - case 3: $header[] = 'Importance: Normal'. $rn; break; - case 5: $header[] = 'Importance: Low'. $rn; break; + case 1: + $header[] = 'X-Priority: 1 (Highest)'.$rn; + $header[] = 'Importance: High'. $rn; break; + case 5: + $header[] = 'X-Priority: 5 (Lowest)'.$rn; + $header[] = 'Importance: Low'. $rn; break; default: break; } } @@ -495,7 +685,9 @@ class Deliver { $aRefs = explode(' ',$sRefs); $sLine = 'References:'; foreach ($aRefs as $sReference) { - if (strlen($sLine)+strlen($sReference) >76) { + if ( trim($sReference) == '' ) { + /* Don't add spaces. */ + } elseif (strlen($sLine)+strlen($sReference) >76) { $hdr_s .= $sLine; $sLine = $rn . ' ' . $sReference; } else { @@ -504,6 +696,12 @@ class Deliver { } $hdr_s .= $sLine; break; + case 'To': + case 'Cc': + case 'Bcc': + case 'From': + $hdr_s .= $header[$i]; + break; default: $hdr_s .= $this->foldLine($header[$i], 78, str_pad('',4)); break; } } @@ -520,7 +718,7 @@ class Deliver { * @param integer $length length to fold the line at * @param string $pre prefix the line with... * - * @returns string $line folded line with trailing CRLF + * @return string $line folded line with trailing CRLF */ function foldLine($line, $length, $pre='') { $line = substr($line,0, -2); @@ -613,7 +811,7 @@ class Deliver { * This function will generate a random mime boundary base part * for the message if the boundary has not already been set. * - * @returns string $mimeBoundaryString random mime boundary string + * @return string $mimeBoundaryString random mime boundary string */ function mimeBoundary () { static $mimeBoundaryString; @@ -629,7 +827,7 @@ class Deliver { /** * function timezone - Time offset for correct timezone * - * @returns string $result with timezone and offset + * @return string $result with timezone and offset */ function timezone () { global $invert_time; @@ -653,27 +851,96 @@ class Deliver { } /** - * function calculate_references - calculate correct Referer string + * function calculate_references - calculate correct References string + * Adds the current message ID, and makes sure it doesn't grow forever, + * to that extent it drops message-ID's in a smart way until the string + * length is under the recommended value of 1000 ("References: <986>\r\n"). + * It always keeps the first and the last three ID's. * * @param Rfc822Header $hdr message header to calculate from * - * @returns string $refer concatenated and trimmed Referer string + * @return string $refer concatenated and trimmed References string */ function calculate_references($hdr) { - $refer = $hdr->references; + $aReferences = preg_split('/\s+/', $hdr->references); $message_id = $hdr->message_id; $in_reply_to = $hdr->in_reply_to; - if (strlen($refer) > 2) { - $refer .= ' ' . $message_id; - } else { - if ($in_reply_to) { - $refer .= $in_reply_to . ' ' . $message_id; - } else { - $refer .= $message_id; + + // if References already exists, add the current message ID at the end. + // no References exists; if we know a IRT, add that aswell + if (count($aReferences) == 0 && $in_reply_to) { + $aReferences[] = $in_reply_to; + } + $aReferences[] = $message_id; + + // sanitize the array: trim whitespace, remove dupes + array_walk($aReferences, 'sq_trim_value'); + $aReferences = array_unique($aReferences); + + while ( count($aReferences) > 4 && strlen(implode(' ', $aReferences)) >= 986 ) { + $aReferences = array_merge(array_slice($aReferences,0,1),array_slice($aReferences,2)); + } + return implode(' ', $aReferences); + } + + /** + * Converts ip address to hexadecimal string + * + * Function is used to convert ipv4 and ipv6 addresses to hex strings. + * It removes all delimiter symbols from ip addresses, converts decimal + * ipv4 numbers to hex and pads strings in order to present full length + * address. ipv4 addresses are represented as 8 byte strings, ipv6 addresses + * are represented as 32 byte string. + * + * If function fails to detect address format, it returns unprocessed string. + * @param string $string ip address string + * @return string processed ip address string + * @since 1.5.1 and 1.4.5 + */ + function ip2hex($string) { + if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/",$string,$match)) { + // ipv4 address + $ret = str_pad(dechex($match[1]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[2]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[3]),2,'0',STR_PAD_LEFT) + . str_pad(dechex($match[4]),2,'0',STR_PAD_LEFT); + } elseif (preg_match("/^([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)\:([0-9a-h]+)$/i",$string,$match)) { + // full ipv6 address + $ret = str_pad($match[1],4,'0',STR_PAD_LEFT) + . str_pad($match[2],4,'0',STR_PAD_LEFT) + . str_pad($match[3],4,'0',STR_PAD_LEFT) + . str_pad($match[4],4,'0',STR_PAD_LEFT) + . str_pad($match[5],4,'0',STR_PAD_LEFT) + . str_pad($match[6],4,'0',STR_PAD_LEFT) + . str_pad($match[7],4,'0',STR_PAD_LEFT) + . str_pad($match[8],4,'0',STR_PAD_LEFT); + } elseif (preg_match("/^\:\:([0-9a-h\:]+)$/i",$string,$match)) { + // short ipv6 with all starting symbols nulled + $aAddr=explode(':',$match[1]); + $ret=''; + foreach ($aAddr as $addr) { + $ret.=str_pad($addr,4,'0',STR_PAD_LEFT); } + $ret=str_pad($ret,32,'0',STR_PAD_LEFT); + } elseif (preg_match("/^([0-9a-h\:]+)::([0-9a-h\:]+)$/i",$string,$match)) { + // short ipv6 with middle part nulled + $aStart=explode(':',$match[1]); + $sStart=''; + foreach($aStart as $addr) { + $sStart.=str_pad($addr,4,'0',STR_PAD_LEFT); + } + $aEnd = explode(':',$match[2]); + $sEnd=''; + foreach($aEnd as $addr) { + $sEnd.=str_pad($addr,4,'0',STR_PAD_LEFT); + } + $ret = $sStart + . str_pad('',(32 - strlen($sStart . $sEnd)),'0',STR_PAD_LEFT) + . $sEnd; + } else { + // unknown addressing + $ret = $string; } - trim($refer); - return $refer; + return $ret; } } -?> \ No newline at end of file