Source for file Deliver.class.php

Documentation is available at Deliver.class.php

  1. <?php
  2.  
  3. /**
  4.  * Deliver.class.php
  5.  *
  6.  * This contains all the functions needed to send messages through
  7.  * a delivery backend.
  8.  *
  9.  * @author Marc Groot Koerkamp
  10.  * @copyright 1999-2020 The SquirrelMail Project Team
  11.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  12.  * @version $Id: Deliver.class.php 14845 2020-01-07 08:09:34Z pdontthink $
  13.  * @package squirrelmail
  14.  */
  15.  
  16. /**
  17.  * Deliver Class - called to actually deliver the message
  18.  *
  19.  * This class is called by compose.php and other code that needs
  20.  * to send messages.  All delivery functionality should be centralized
  21.  * in this class.
  22.  *
  23.  * Do not place UI code in this class, as UI code should be placed in templates
  24.  * going forward.
  25.  *
  26.  * @author  Marc Groot Koerkamp
  27.  * @package squirrelmail
  28.  */
  29. class Deliver {
  30.  
  31.     /**
  32.      * function mail - send the message parts to the SMTP stream
  33.      *
  34.      * @param Message  $message      Message object to send
  35.      *                                NOTE that this is passed by
  36.      *                                reference and will be modified
  37.      *                                upon return with updated
  38.      *                                fields such as Message ID, References,
  39.      *                                In-Reply-To and Date headers.
  40.      * @param resource $stream       Handle to the outgoing stream
  41.      *                                (when FALSE, nothing will be
  42.      *                                written to the stream; this can
  43.      *                                be used to determine the actual
  44.      *                                number of bytes that will be
  45.      *                                written to the stream)
  46.      * @param string   $reply_id     Identifies message being replied to
  47.      *                                (OPTIONAL; caller should ONLY specify
  48.      *                                a value for this when the message
  49.      *                                being sent is a reply)
  50.      * @param string   $reply_ent_id Identifies message being replied to
  51.      *                                in the case it was an embedded/attached
  52.      *                                message inside another (OPTIONAL; caller
  53.      *                                should ONLY specify a value for this
  54.      *                                when the message being sent is a reply)
  55.      * @param resource $imap_stream  If there is an open IMAP stream in
  56.      *                                the caller's context, it should be
  57.      *                                passed in here.  This is OPTIONAL,
  58.      *                                as one will be created if not given,
  59.      *                                but as some IMAP servers may baulk
  60.      *                                at opening more than one connection
  61.      *                                at a time, the caller should always
  62.      *                                abide if possible.  Currently, this
  63.      *                                stream is only used when $reply_id
  64.      *                                is also non-zero, but that is subject
  65.      *                                to change.
  66.      * @param mixed    $extra        Any implementation-specific variables
  67.      *                                can be passed in here and used in
  68.      *                                an overloaded version of this method
  69.      *                                if needed.
  70.      *
  71.      * @return integer The number of bytes written (or that would have been
  72.      *                  written) to the output stream.
  73.      *
  74.      */
  75.     function mail(&$message$stream=false$reply_id=0$reply_ent_id=0,
  76.                   $imap_stream=NULL$extra=NULL{
  77.  
  78.         $rfc822_header &$message->rfc822_header;
  79.  
  80.         if (count($message->entities)) {
  81.             $boundary $this->mimeBoundary();
  82.             $rfc822_header->content_type->properties['boundary']='"'.$boundary.'"';
  83.         else {
  84.             $boundary='';
  85.         }
  86.         $raw_length 0;
  87.  
  88.  
  89.         // calculate reply header if needed
  90.         //
  91.         if ($reply_id{
  92.             global $imapConnection$username$imapServerAddress
  93.                    $imapPort$imap_stream_options$mailbox;
  94.  
  95.             // try our best to use an existing IMAP handle
  96.             //
  97.             $close_imap_stream FALSE;
  98.             if (is_resource($imap_stream)) {
  99.                 $my_imap_stream $imap_stream;
  100.  
  101.             else if (is_resource($imapConnection)) {
  102.                 $my_imap_stream $imapConnection;
  103.  
  104.             else {
  105.                 $close_imap_stream TRUE;
  106.                 $my_imap_stream sqimap_login($usernameFALSE$imapServerAddress,
  107.                                                $imapPort0$imap_stream_options);
  108.             }
  109.  
  110.             sqimap_mailbox_select($my_imap_stream$mailbox);
  111.             $reply_message sqimap_get_message($my_imap_stream$reply_id$mailbox);
  112.  
  113.             if ($close_imap_stream{
  114.                 sqimap_logout($my_imap_stream);
  115.             }
  116.  
  117.             if ($reply_ent_id{
  118.                 /* redefine the messsage in case of message/rfc822 */
  119.                 $reply_message $message->getEntity($reply_ent_id);
  120.                 /* message is an entity which contains the envelope and type0=message
  121.                  * and type1=rfc822. The actual entities are childs from
  122.                  * $reply_message->entities[0]. That's where the encoding and is located
  123.                  */
  124.  
  125.                 $orig_header $reply_message->rfc822_header/* here is the envelope located */
  126.  
  127.             else {
  128.                 $orig_header $reply_message->rfc822_header;
  129.             }
  130.             $message->reply_rfc822_header $orig_header;
  131.         }
  132.  
  133.  
  134.         $reply_rfc822_header (isset($message->reply_rfc822_header)
  135.                              ? $message->reply_rfc822_header '');
  136.         $header $this->prepareRFC822_Header($rfc822_header$reply_rfc822_header$raw_length);
  137.  
  138.         $this->send_mail($message$header$boundary$stream$raw_length$extra);
  139.  
  140.         return $raw_length;
  141.     }
  142.  
  143.     /**
  144.      * function send_mail - send the message parts to the IMAP stream
  145.      *
  146.      * @param Message  $message      Message object to send
  147.      * @param string   $header       Headers ready to send
  148.      * @param string   $boundary     Message parts boundary
  149.      * @param resource $stream       Handle to the SMTP stream
  150.      *                                (when FALSE, nothing will be
  151.      *                                written to the stream; this can
  152.      *                                be used to determine the actual
  153.      *                                number of bytes that will be
  154.      *                                written to the stream)
  155.      * @param int     &$raw_length   The number of bytes written (or that
  156.      *                                would have been written) to the
  157.      *                                output stream - NOTE that this is
  158.      *                                passed by reference
  159.      * @param mixed    $extra        Any implementation-specific variables
  160.      *                                can be passed in here and used in
  161.      *                                an overloaded version of this method
  162.      *                                if needed.
  163.      *
  164.      * @return void 
  165.      *
  166.      */
  167.     function send_mail($message$header$boundary$stream=false
  168.                        &$raw_length$extra=NULL{
  169.  
  170.  
  171.         if ($stream{
  172.             $this->preWriteToStream($header);
  173.             $this->writeToStream($stream$header);
  174.         }
  175.         $this->writeBody($message$stream$raw_length$boundary);
  176.     }
  177.  
  178.     /**
  179.      * function writeBody - generate and write the mime boundaries around each part to the stream
  180.      *
  181.      * Recursively formats and writes the MIME boundaries of the $message
  182.      * to the output stream.
  183.      *
  184.      * @param Message   $message      Message object to transform
  185.      * @param resource  $stream       SMTP output stream
  186.      *                                 (when FALSE, nothing will be
  187.      *                                 written to the stream; this can
  188.      *                                 be used to determine the actual
  189.      *                                 number of bytes that will be
  190.      *                                 written to the stream)
  191.      * @param integer  &$length_raw   raw length of the message (part)
  192.      *                                 as returned by mail fn
  193.      * @param string    $boundary     custom boundary to call, usually for subparts
  194.      *
  195.      * @return void 
  196.      */
  197.     function writeBody($message$stream&$length_raw$boundary=''{
  198.         // calculate boundary in case of multidimensional mime structures
  199.         if ($boundary && $message->entity_id && count($message->entities)) {
  200.             if (strpos($boundary,'_part_')) {
  201.                 $boundary substr($boundary,0,strpos($boundary,'_part_'));
  202.  
  203.             // the next four lines use strrev to reverse any nested boundaries
  204.             // because RFC 2046 (5.1.1) says that if a line starts with the outer
  205.             // boundary string (doesn't matter what the line ends with), that
  206.             // can be considered a match for the outer boundary; thus the nested
  207.             // boundary needs to be unique from the outer one
  208.             //
  209.             else if (strpos($boundary,'_trap_')) {
  210.                 $boundary substr(strrev($boundary),0,strpos(strrev($boundary),'_part_'));
  211.             }
  212.             $boundary_new strrev($boundary '_part_'.$message->entity_id);
  213.         else {
  214.             $boundary_new $boundary;
  215.         }
  216.         if ($boundary && !$message->rfc822_header{
  217.             $s '--'.$boundary."\r\n";
  218.             $s .= $this->prepareMIME_Header($message$boundary_new);
  219.             $length_raw += strlen($s);
  220.             if ($stream{
  221.                 $this->preWriteToStream($s);
  222.                 $this->writeToStream($stream$s);
  223.             }
  224.         }
  225.         $this->writeBodyPart($message$stream$length_raw);
  226.  
  227.         $last false;
  228.         for ($i=0$entCount=count($message->entities);$i<$entCount;$i++{
  229.             $msg $this->writeBody($message->entities[$i]$stream$length_raw$boundary_new);
  230.             if ($i == $entCount-1$last true;
  231.         }
  232.         if ($boundary && $last{
  233.             $s "--".$boundary_new."--\r\n\r\n";
  234.             $length_raw += strlen($s);
  235.             if ($stream{
  236.                 $this->preWriteToStream($s);
  237.                 $this->writeToStream($stream$s);
  238.             }
  239.         }
  240.     }
  241.  
  242.     /**
  243.      * function writeBodyPart - write each individual mimepart
  244.      *
  245.      * Recursively called by WriteBody to write each mime part to the SMTP stream
  246.      *
  247.      * @param Message   $message      Message object to transform
  248.      * @param resource  $stream       SMTP output stream
  249.      *                                 (when FALSE, nothing will be
  250.      *                                 written to the stream; this can
  251.      *                                 be used to determine the actual
  252.      *                                 number of bytes that will be
  253.      *                                 written to the stream)
  254.      * @param integer  &$length       length of the message part
  255.      *                                 as returned by mail fn
  256.      *
  257.      * @return void 
  258.      */
  259.     function writeBodyPart($message$stream&$length{
  260.         if ($message->mime_header{
  261.             $type0 $message->mime_header->type0;
  262.         else {
  263.             $type0 $message->rfc822_header->content_type->type0;
  264.         }
  265.  
  266.         $body_part_trailing $last '';
  267.         switch ($type0)
  268.         {
  269.         case 'text':
  270.         case 'message':
  271.             if ($message->body_part{
  272.                 $body_part $message->body_part;
  273.                 // remove NUL characters
  274.                 $body_part str_replace("\0",'',$body_part);
  275.                 $length += $this->clean_crlf($body_part);
  276.                 if ($stream{
  277.                     $this->preWriteToStream($body_part);
  278.                     $this->writeToStream($stream$body_part);
  279.                 }
  280.                 $last $body_part;
  281.             elseif ($message->att_local_name{
  282.                 global $username$attachment_dir;
  283.                 $hashed_attachment_dir getHashedDir($username$attachment_dir);
  284.                 $filename $message->att_local_name;
  285.  
  286.                 // inspect attached file for lines longer than allowed by RFC,
  287.                 // in which case we'll be using base64 encoding (so we can split
  288.                 // the lines up without corrupting them) instead of 8bit unencoded...
  289.                 // (see RFC 2822/2.1.1)
  290.                 //
  291.                 // using 990 because someone somewhere is folding lines at
  292.                 // 990 instead of 998 and I'm too lazy to find who it is
  293.                 //
  294.                 $file_has_long_lines file_has_long_lines($hashed_attachment_dir
  295.                                                            . '/' $filename990);
  296.  
  297.                 $file fopen ($hashed_attachment_dir '/' $filename'rb');
  298.  
  299.                 // long lines were found, need to use base64 encoding
  300.                 //
  301.                 if ($file_has_long_lines{
  302.                     while ($tmp fread($file570)) {
  303.                         $body_part chunk_split(base64_encode($tmp));
  304.                         // Up to 4.3.10 chunk_split always appends a newline,
  305.                         // while in 4.3.11 it doesn't if the string to split
  306.                         // is shorter than the chunk length.
  307.                         ifsubstr($body_part-!= "\n" )
  308.                             $body_part .= "\n";
  309.                         $length += $this->clean_crlf($body_part);
  310.                         if ($stream{
  311.                             $this->writeToStream($stream$body_part);
  312.                         }
  313.                     }
  314.                 }
  315.  
  316.                 // no excessively long lines - normal 8bit
  317.                 //
  318.                 else {
  319.                     while ($body_part fgets($file4096)) {
  320.                         // remove NUL characters
  321.                         $body_part str_replace("\0",'',$body_part);
  322.                         $length += $this->clean_crlf($body_part);
  323.                         if ($stream{
  324.                             $this->preWriteToStream($body_part);
  325.                             $this->writeToStream($stream$body_part);
  326.                         }
  327.                         $last $body_part;
  328.                     }
  329.                 }
  330.  
  331.                 fclose($file);
  332.             }
  333.             break;
  334.         default:
  335.             if ($message->body_part{
  336.                 $body_part $message->body_part;
  337.                 // remove NUL characters
  338.                 $body_part str_replace("\0",'',$body_part);
  339.                 $length += $this->clean_crlf($body_part);
  340.                 if ($stream{
  341.                     $this->writeToStream($stream$body_part);
  342.                 }
  343.             elseif ($message->att_local_name{
  344.                 global $username$attachment_dir;
  345.                 $hashed_attachment_dir getHashedDir($username$attachment_dir);
  346.                 $filename $message->att_local_name;
  347.                 $file fopen ($hashed_attachment_dir '/' $filename'rb');
  348.                 while ($tmp fread($file570)) {
  349.                     $body_part chunk_split(base64_encode($tmp));
  350.                     // Up to 4.3.10 chunk_split always appends a newline,
  351.                     // while in 4.3.11 it doesn't if the string to split
  352.                     // is shorter than the chunk length.
  353.                     ifsubstr($body_part-!= "\n" )
  354.                         $body_part .= "\n";
  355.                     $length += $this->clean_crlf($body_part);
  356.                     if ($stream{
  357.                         $this->writeToStream($stream$body_part);
  358.                     }
  359.                 }
  360.                 fclose($file);
  361.             }
  362.             break;
  363.         }
  364.         $body_part_trailing '';
  365.         if ($last && substr($last,-1!= "\n"{
  366.             $body_part_trailing "\r\n";
  367.         }
  368.         if ($body_part_trailing{
  369.             $length += strlen($body_part_trailing);
  370.             if ($stream{
  371.                 $this->preWriteToStream($body_part_trailing);
  372.                 $this->writeToStream($stream$body_part_trailing);
  373.             }
  374.         }
  375.     }
  376.  
  377.     /**
  378.      * function clean_crlf - change linefeeds and newlines to legal characters
  379.      *
  380.      * The SMTP format only allows CRLF as line terminators.
  381.      * This function replaces illegal teminators with the correct terminator.
  382.      *
  383.      * @param string &$s string to clean linefeeds on
  384.      *
  385.      * @return void 
  386.      */
  387.     function clean_crlf(&$s{
  388.         $s str_replace("\r\n""\n"$s);
  389.         $s str_replace("\r""\n"$s);
  390.         $s str_replace("\n""\r\n"$s);
  391.         return strlen($s);
  392.     }
  393.  
  394.     /**
  395.      * function strip_crlf - strip linefeeds and newlines from a string
  396.      *
  397.      * The SMTP format only allows CRLF as line terminators.
  398.      * This function strips all line terminators from the string.
  399.      *
  400.      * @param string &$s string to clean linefeeds on
  401.      *
  402.      * @return void 
  403.      */
  404.     function strip_crlf(&$s{
  405.         $s str_replace("\r\n "''$s);
  406.         $s str_replace("\r"''$s);
  407.         $s str_replace("\n"''$s);
  408.     }
  409.  
  410.     /**
  411.      * function preWriteToStream - reserved for extended functionality
  412.      *
  413.      * This function is not yet implemented.
  414.      * Reserved for extended functionality.
  415.      *
  416.      * @param string &$s string to operate on
  417.      *
  418.      * @return void 
  419.      */
  420.     function preWriteToStream(&$s{
  421.     }
  422.  
  423.     /**
  424.      * function writeToStream - write data to the SMTP stream
  425.      *
  426.      * @param resource $stream  SMTP output stream
  427.      * @param string   $data    string with data to send to the SMTP stream
  428.      *
  429.      * @return void 
  430.      */
  431.     function writeToStream($stream$data{
  432.         fputs($stream$data);
  433.     }
  434.  
  435.     /**
  436.      * function initStream - reserved for extended functionality
  437.      *
  438.      * This function is not yet implemented.
  439.      * Reserved for extended functionality.
  440.      * UPDATE: It is implemented in Deliver_SMTP and Deliver_SendMail classes,
  441.      *         but it remains unimplemented in this base class (and thus not
  442.      *         in Deliver_IMAP or other child classes that don't define it)
  443.      *
  444.      * NOTE: some parameters are specific to the child class
  445.      *       that is implementing this method
  446.      *
  447.      * @param Message $message  Message object
  448.      * @param string  $domain 
  449.      * @param integer $length 
  450.      * @param string  $host     host name or IP to connect to
  451.      * @param integer $port 
  452.      * @param string  $user     username to log into the SMTP server with
  453.      * @param string  $pass     password to log into the SMTP server with
  454.      * @param boolean $authpop  whether or not to use POP-before-SMTP authorization
  455.      * @param string  $pop_host host name or IP to connect to for POP-before-SMTP authorization
  456.      * @param array   $stream_options Stream context options, see config_local.example.php for more details (OPTIONAL)
  457.      *
  458.      * @return handle $stream file handle resource to SMTP stream
  459.      */
  460.     function initStream($message$domain$length=0$host=''$port=''$user=''$pass=''$authpop=false$pop_host=''$stream_options=array()) {
  461.         return $stream;
  462.     }
  463.  
  464.     /**
  465.      * function getBCC - reserved for extended functionality
  466.      *
  467.      * This function is not yet implemented.
  468.      * Reserved for extended functionality.
  469.      *
  470.      */
  471.     function getBCC({
  472.         return false;
  473.     }
  474.  
  475.     /**
  476.      * function prepareMIME_Header - creates the mime header
  477.      *
  478.      * @param Message $message  Message object to act on
  479.      * @param string  $boundary mime boundary from fn MimeBoundary
  480.      *
  481.      * @return string $header properly formatted mime header
  482.      */
  483.     function prepareMIME_Header($message$boundary{
  484.         $mime_header $message->mime_header;
  485.         $rn="\r\n";
  486.         $header array();
  487.  
  488.         $contenttype 'Content-Type: '$mime_header->type0 .'/'.
  489.                         $mime_header->type1;
  490.         if (count($message->entities)) {
  491.             $contenttype .= ';' 'boundary="'.$boundary.'"';
  492.         }
  493.         if (isset($mime_header->parameters['name'])) {
  494.             $contenttype .= '; name="'.
  495.             encodeHeader($mime_header->parameters['name'])'"';
  496.         }
  497.         if (isset($mime_header->parameters['charset'])) {
  498.             $charset $mime_header->parameters['charset'];
  499.             $contenttype .= '; charset="'.
  500.             encodeHeader($charset)'"';
  501.         }
  502.  
  503.         $header[$contenttype $rn;
  504.         if ($mime_header->description{
  505.             $header['Content-Description: ' $mime_header->description $rn;
  506.         }
  507.         if ($mime_header->encoding{
  508.             $header['Content-Transfer-Encoding: ' $mime_header->encoding $rn;
  509.         else {
  510.  
  511.             // inspect attached file for lines longer than allowed by RFC,
  512.             // in which case we'll be using base64 encoding (so we can split
  513.             // the lines up without corrupting them) instead of 8bit unencoded...
  514.             // (see RFC 2822/2.1.1)
  515.             //
  516.             if (!empty($message->att_local_name)) // is this redundant? I have no idea
  517.                 global $username$attachment_dir;
  518.                 $hashed_attachment_dir getHashedDir($username$attachment_dir);
  519.                 $filename $hashed_attachment_dir '/' $message->att_local_name;
  520.  
  521.                 // using 990 because someone somewhere is folding lines at
  522.                 // 990 instead of 998 and I'm too lazy to find who it is
  523.                 //
  524.                 $file_has_long_lines file_has_long_lines($filename990);
  525.             else
  526.                 $file_has_long_lines FALSE;
  527.  
  528.             if ($mime_header->type0 == 'multipart' || $mime_header->type0 == 'alternative'{
  529.                 /* no-op; no encoding needed */
  530.             else if (($mime_header->type0 == 'text' || $mime_header->type0 == 'message')
  531.                     && !$file_has_long_lines{
  532.                 $header['Content-Transfer-Encoding: 8bit' .  $rn;
  533.             else {
  534.                 $header['Content-Transfer-Encoding: base64' .  $rn;
  535.             }
  536.         }
  537.         if ($mime_header->id{
  538.             $header['Content-ID: ' $mime_header->id $rn;
  539.         }
  540.         if ($mime_header->disposition{
  541.             $disposition $mime_header->disposition;
  542.             $contentdisp 'Content-Disposition: ' $disposition->name;
  543.             if ($disposition->getProperty('filename')) {
  544.                 $contentdisp .= '; filename="'.
  545.                 encodeHeader($disposition->getProperty('filename'))'"';
  546.             }
  547.             $header[$contentdisp $rn;
  548.         }
  549.         if ($mime_header->md5{
  550.             $header['Content-MD5: ' $mime_header->md5 $rn;
  551.         }
  552.         if ($mime_header->language{
  553.             $header['Content-Language: ' $mime_header->language $rn;
  554.         }
  555.  
  556.         $cnt count($header);
  557.         $hdr_s '';
  558.         for ($i $i $cnt $i++)    {
  559.             $hdr_s .= $this->foldLine($header[$i]78);
  560.         }
  561.         $header $hdr_s;
  562.         $header .= $rn/* One blank line to separate mimeheader and body-entity */
  563.         return $header;
  564.     }
  565.  
  566.     /**
  567.      * function prepareRFC822_Header - prepares the RFC822 header string from Rfc822Header object(s)
  568.      *
  569.      * This function takes the Rfc822Header object(s) and formats them
  570.      * into the RFC822Header string to send to the SMTP server as part
  571.      * of the SMTP message.
  572.      *
  573.      * @param Rfc822Header  $rfc822_header 
  574.      * @param Rfc822Header  $reply_rfc822_header 
  575.      * @param integer      &$raw_length length of the message
  576.      *
  577.      * @return string $header
  578.      */
  579.     function prepareRFC822_Header(&$rfc822_header$reply_rfc822_header&$raw_length{
  580.         global $domain$username$encode_header_key,
  581.                $edit_identity$hide_auth_header;
  582.  
  583.         /* if server var SERVER_NAME not available, or contains
  584.            ":" (e.g. IPv6) which is illegal in a Message-ID, use $domain */
  585.         if(!sqGetGlobalVar('SERVER_NAME'$SERVER_NAMESQ_SERVER||
  586.             strpos($SERVER_NAME,':'!== FALSE{
  587.             $SERVER_NAME $domain;
  588.         }
  589.  
  590.         sqGetGlobalVar('REMOTE_ADDR'$REMOTE_ADDRSQ_SERVER);
  591.         sqGetGlobalVar('REMOTE_PORT'$REMOTE_PORTSQ_SERVER);
  592.         sqGetGlobalVar('REMOTE_HOST'$REMOTE_HOSTSQ_SERVER);
  593.         sqGetGlobalVar('HTTP_VIA',    $HTTP_VIA,    SQ_SERVER);
  594.         sqGetGlobalVar('HTTP_X_FORWARDED_FOR'$HTTP_X_FORWARDED_FORSQ_SERVER);
  595.  
  596.         $rn "\r\n";
  597.  
  598.         /* This creates an RFC 822 date */
  599.         $now time();
  600.         $now_date date('D, j M Y H:i:s '$now$this->timezone();
  601.         // TODO: Do we really want to preserve possibly old date?  Date header should always have "now"... but here is not where this decision should be made -- the caller really should blank out $rfc822_header->date even for drafts being re-edited or sent
  602.         if (!empty($rfc822_header->date&& $rfc822_header->date != -1)
  603.             $message_date date('D, j M Y H:i:s '$rfc822_header->date$this->timezone();
  604.         else {
  605.             $message_date $now_date;
  606.             $rfc822_header->date $now;
  607.         }
  608.  
  609.         /* Create a message-id */
  610.         $message_id 'MESSAGE ID GENERATION ERROR! PLEASE CONTACT SQUIRRELMAIL DEVELOPERS';
  611.         if (empty($rfc822_header->message_id)) {
  612.             $message_id '<'
  613.                         . md5(GenerateRandomString(16''7uniqid(mt_rand(),true))
  614.                         . '.squirrel@' $SERVER_NAME .'>';
  615.         }
  616.  
  617.         /* Make an RFC822 Received: line */
  618.         if (isset($REMOTE_HOST)) {
  619.             $received_from "$REMOTE_HOST ([$REMOTE_ADDR])";
  620.         else {
  621.             $received_from $REMOTE_ADDR;
  622.         }
  623.         if (isset($HTTP_VIA|| isset ($HTTP_X_FORWARDED_FOR)) {
  624.             if (!isset($HTTP_X_FORWARDED_FOR|| $HTTP_X_FORWARDED_FOR == ''{
  625.                 $HTTP_X_FORWARDED_FOR 'unknown';
  626.             }
  627.             $received_from .= " (proxying for $HTTP_X_FORWARDED_FOR)";
  628.         }
  629.         $header array();
  630.  
  631.         /**
  632.          * SquirrelMail header
  633.          *
  634.          * This Received: header provides information that allows to track
  635.          * user and machine that was used to send email. Don't remove it
  636.          * unless you understand all possible forging issues or your
  637.          * webmail installation does not prevent changes in user's email address.
  638.          * See SquirrelMail bug tracker #847107 for more details about it.
  639.          *
  640.          * Add hide_squirrelmail_header as a candidate for config_local.php
  641.          * (must be defined as a constant:  define('hide_squirrelmail_header', 1);
  642.          * to allow completely hiding SquirrelMail participation in message
  643.          * processing; This is dangerous, especially if users can modify their
  644.          * account information, as it makes mapping a sent message back to the
  645.          * original sender almost impossible.
  646.          */
  647.         $show_sm_header defined('hide_squirrelmail_header'hide_squirrelmail_header );
  648.  
  649.         // 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 
  650.         if $show_sm_header {
  651.           if (isset($encode_header_key&&
  652.             trim($encode_header_key)!=''{
  653.             // use encoded headers, if encryption key is set and not empty
  654.             $header['X-Squirrel-UserHash: '.OneTimePadEncrypt($username,base64_encode($encode_header_key)).$rn;
  655.             $header['X-Squirrel-FromHash: '.OneTimePadEncrypt($this->ip2hex($REMOTE_ADDR),base64_encode($encode_header_key)).$rn;
  656.             if (isset($HTTP_X_FORWARDED_FOR))
  657.                 $header['X-Squirrel-ProxyHash:'.OneTimePadEncrypt($this->ip2hex($HTTP_X_FORWARDED_FOR),base64_encode($encode_header_key)).$rn;
  658.           else {
  659.             // use default received headers
  660.             $header["Received: from $received_from$rn;
  661.             if (!isset($hide_auth_header|| !$hide_auth_header)
  662.                 $header["        (SquirrelMail authenticated user $username)$rn;
  663.             $header["        by $SERVER_NAME with HTTP;$rn;
  664.             $header["        $now_date$rn;
  665.           }
  666.         }
  667.  
  668.         /* Insert the rest of the header fields */
  669.  
  670.         if (!empty($rfc822_header->message_id)) {
  671.             $header['Message-ID: '$rfc822_header->message_id $rn;
  672.         else {
  673.             $header['Message-ID: '$message_id $rn;
  674.             $rfc822_header->message_id $message_id;
  675.         }
  676.  
  677.         if (is_object($reply_rfc822_header&&
  678.             isset($reply_rfc822_header->message_id&&
  679.             $reply_rfc822_header->message_id{
  680.             $rep_message_id $reply_rfc822_header->message_id;
  681.             $header['In-Reply-To: '.$rep_message_id $rn;
  682.             $rfc822_header->in_reply_to $rep_message_id;
  683.             $references $this->calculate_references($reply_rfc822_header);
  684.             $header['References: '.$references $rn;
  685.             $rfc822_header->references $references;
  686.         }
  687.  
  688.         $header["Date: $message_date$rn;
  689.  
  690.         $header['Subject: '.encodeHeader($rfc822_header->subject$rn;
  691.         $header['From: '$rfc822_header->getAddr_s('from',",$rn ",true$rn;
  692.  
  693.         // folding address list [From|To|Cc|Bcc] happens by using ",$rn<space>"
  694.         // as delimiter
  695.         // Do not use foldLine for that.
  696.  
  697.         // RFC2822 if from contains more then 1 address
  698.         if (count($rfc822_header->from1{
  699.             $header['Sender: '$rfc822_header->getAddr_s('sender',',',true$rn;
  700.         }
  701.         if (count($rfc822_header->to)) {
  702.             $header['To: '$rfc822_header->getAddr_s('to',",$rn ",true$rn;
  703.         }
  704.         if (count($rfc822_header->cc)) {
  705.             $header['Cc: '$rfc822_header->getAddr_s('cc',",$rn ",true$rn;
  706.         }
  707.         if (count($rfc822_header->reply_to)) {
  708.             $header['Reply-To: '$rfc822_header->getAddr_s('reply_to',',',true$rn;
  709.         }
  710.         /* Sendmail should return true. Default = false */
  711.         $bcc $this->getBcc();
  712.         if (count($rfc822_header->bcc)) {
  713.             $s 'Bcc: '$rfc822_header->getAddr_s('bcc',",$rn ",true$rn;
  714.             if (!$bcc{
  715.                 $raw_length += strlen($s);
  716.             else {
  717.                 $header[$s;
  718.             }
  719.         }
  720.         /* Identify SquirrelMail */
  721.         $header['User-Agent: SquirrelMail/' SM_VERSION $rn;
  722.         /* Do the MIME-stuff */
  723.         $header['MIME-Version: 1.0' $rn;
  724.         $contenttype 'Content-Type: '$rfc822_header->content_type->type0 .'/'.
  725.                                          $rfc822_header->content_type->type1;
  726.         if (count($rfc822_header->content_type->properties)) {
  727.             foreach ($rfc822_header->content_type->properties as $k => $v{
  728.                 if ($k && $v{
  729.                     $contenttype .= ';' .$k.'='.$v;
  730.                 }
  731.             }
  732.         }
  733.         $header[$contenttype $rn;
  734.         if ($encoding $rfc822_header->encoding{
  735.             $header['Content-Transfer-Encoding: ' $encoding .  $rn;
  736.         }
  737.         if (isset($rfc822_header->dnt&& $rfc822_header->dnt{
  738.             $dnt $rfc822_header->getAddr_s('dnt');
  739.             /* Pegasus Mail */
  740.             $header['X-Confirm-Reading-To: '.$dnt$rn;
  741.             /* RFC 2298 */
  742.             $header['Disposition-Notification-To: '.$dnt$rn;
  743.         }
  744.         if ($rfc822_header->priority{
  745.             switch($rfc822_header->priority)
  746.             {
  747.             case 1:
  748.                 $header['X-Priority: 1 (Highest)'.$rn;
  749.                 $header['Importance: High'$rnbreak;
  750.             case 5:
  751.                 $header['X-Priority: 5 (Lowest)'.$rn;
  752.                 $header['Importance: Low'$rnbreak;
  753.             defaultbreak;
  754.             }
  755.         }
  756.         /* Insert headers from the $more_headers array */
  757.         if(count($rfc822_header->more_headers)) {
  758.             reset($rfc822_header->more_headers);
  759.             foreach ($rfc822_header->more_headers as $k => $v{
  760.                 $header[$k.': '.$v .$rn;
  761.             }
  762.         }
  763.         $cnt count($header);
  764.         $hdr_s '';
  765.  
  766.         for ($i $i $cnt $i++{
  767.             $sKey substr($header[$i],0,strpos($header[$i],':'));
  768.             switch ($sKey)
  769.             {
  770.             case 'Message-ID':
  771.             case 'In-Reply_To':
  772.                 $hdr_s .= $header[$i];
  773.                 break;
  774.             case 'References':
  775.                 $sRefs substr($header[$i],12);
  776.                 $aRefs explode(' ',$sRefs);
  777.                 $sLine 'References:';
  778.                 foreach ($aRefs as $sReference{
  779.                     if trim($sReference== '' {
  780.                          /* Don't add spaces. */
  781.                     elseif (strlen($sLine)+strlen($sReference>76{
  782.                         $hdr_s .= $sLine;
  783.                         $sLine $rn '    ' $sReference;
  784.                     else {
  785.                         $sLine .= ' '$sReference;
  786.                     }
  787.                 }
  788.                 $hdr_s .= $sLine;
  789.                 break;
  790.             case 'To':
  791.             case 'Cc':
  792.             case 'Bcc':
  793.             case 'From':
  794.                 $hdr_s .= $header[$i];
  795.                 break;
  796.             default$hdr_s .= $this->foldLine($header[$i]78)break;
  797.             }
  798.         }
  799.         $header $hdr_s;
  800.         $header .= $rn/* One blank line to separate header and body */
  801.         $raw_length += strlen($header);
  802.         return $header;
  803.     }
  804.  
  805.     /**
  806.       * Fold header lines per RFC 2822/2.2.3 and RFC 822/3.1.1
  807.       *
  808.       * Herein "soft" folding/wrapping (with whitespace tokens) is
  809.       * what we refer to as the preferred method of wrapping - that
  810.       * which we'd like to do within the $soft_wrap limit, but if
  811.       * not possible, we will try to do as soon as possible after
  812.       * $soft_wrap up to the $hard_wrap limit.  Encoded words don't
  813.       * need to be detected in this phase, since they cannot contain
  814.       * spaces.
  815.       *
  816.       * "Hard" folding/wrapping (with "hard" tokens) is what we refer
  817.       * to as less ideal wrapping that will be done to keep within
  818.       * the $hard_wrap limit.  This adds other syntactical breaking
  819.       * elements such as commas and encoded words.
  820.       *
  821.       * @param string  $header    The header content being folded
  822.       * @param integer $soft_wrap The desirable maximum line length
  823.       *                            (OPTIONAL; default is 78, per RFC)
  824.       * @param string  $indent    Wrapped lines will already have
  825.       *                            whitespace following the CRLF wrap,
  826.       *                            but you can add more indentation (or
  827.       *                            whatever) with this.  The use of this
  828.       *                            parameter is DISCOURAGED, since it
  829.       *                            can corrupt the redisplay (unfolding)
  830.       *                            of headers whose content is space-
  831.       *                            sensitive, like subjects, etc.
  832.       *                            (OPTIONAL; default is an empty string)
  833.       * @param string  $hard_wrap The absolute maximum line length
  834.       *                            (OPTIONAL; default is 998, per RFC)
  835.       *
  836.       * @return string The folded header content, with a trailing CRLF.
  837.       *
  838.       */
  839.     function foldLine($header$soft_wrap=78$indent=''$hard_wrap=998{
  840.  
  841.         // allow folding after the initial colon and space?
  842.         // (only supported if the header name is within the $soft_wrap limit)
  843.         //
  844.         $allow_fold_after_header_name FALSE;
  845.  
  846.         // the "hard" token list can be altered if desired,
  847.         // for example, by adding ":"
  848.         // (in the future, we can take optional arguments
  849.         // for overriding or adding elements to the "hard"
  850.         // token list if we want to get fancy)
  851.         //
  852.         // the order of these is significant - preferred
  853.         // fold points should be listed first
  854.         //
  855.         // it is advised that the "=" always come first
  856.         // since it also finds encoded words, thus if it
  857.         // comes after some other token that happens to
  858.         // fall within the encoded word, the encoded word
  859.         // could be inadvertently broken in half, which
  860.         // is not allowable per RFC
  861.         //
  862.         $hard_break_tokens array(
  863.             '=',  // includes encoded word detection
  864.             ',',
  865.             ';',
  866.         );
  867.  
  868.         // the order of these is significant too
  869.         //
  870.         $whitespace array(
  871.             ' ',
  872.             "\t",
  873.         );
  874.  
  875.         $CRLF "\r\n";
  876.  
  877.         // switch that helps compact the last line, pasting it at the
  878.         // end of the one before if the one before is already over the
  879.         // soft limit and it wouldn't go over the hard limit
  880.         //
  881.         $pull_last_line_up_if_second_to_last_is_already_over_soft_limit FALSE;
  882.  
  883.  
  884.         // ----- end configurable behaviors -----
  885.  
  886.  
  887.         $folded_header '';
  888.  
  889.         // if we want to prevent a wrap right after the
  890.         // header name, make note of the position here
  891.         //
  892.         if (!$allow_fold_after_header_name
  893.          && ($header_name_end_pos strpos($header':'))
  894.          && strlen($header$header_name_end_pos 1
  895.          && in_array($header{$header_name_end_pos 1}$whitespace))
  896.             $header_name_end_pos++;
  897.  
  898.         // if using an indent string, reduce wrap limits by its size
  899.         //
  900.         if (!empty($indent)) {
  901.             $soft_wrap -= strlen($indent);
  902.             $hard_wrap -= strlen($indent);
  903.         }
  904.  
  905.         while (strlen($header$soft_wrap{
  906.  
  907.             $soft_wrapped_line substr($header0$soft_wrap);
  908.  
  909.             // look for a token as close to the end of the soft wrap limit as possible
  910.             //
  911.             foreach ($whitespace as $token{
  912.  
  913.                 // note that this if statement also fails when $pos === 0,
  914.                 // which is intended, since blank lines are not allowed
  915.                 //
  916.                 if ($pos strrpos($soft_wrapped_line$token))
  917.                 {
  918.  
  919.                     // make sure proposed fold isn't forbidden
  920.                     //
  921.                     if (!$allow_fold_after_header_name
  922.                      && $pos === $header_name_end_pos)
  923.                         continue;
  924.  
  925.                     $new_fold substr($header0$pos);
  926.  
  927.                     // make sure proposed fold doesn't create a blank line
  928.                     //
  929.                     if (!trim($new_fold)) continue;
  930.  
  931.                     // with whitespace breaks, we fold BEFORE the token
  932.                     //
  933.                     $folded_header .= $new_fold $CRLF $indent;
  934.                     $header substr($header$pos);
  935.  
  936.                     // ready for next while() iteration
  937.                     //
  938.                     continue 2;
  939.  
  940.                 }
  941.  
  942.             }
  943.  
  944.             // we were unable to find a wrapping point within the soft
  945.             // wrap limit, so now we'll try to find the first possible
  946.             // soft wrap point within the hard wrap limit
  947.             //
  948.             $hard_wrapped_line substr($header0$hard_wrap);
  949.  
  950.             // look for a *SOFT* token as close to the
  951.             // beginning of the hard wrap limit as possible
  952.             //
  953.             foreach ($whitespace as $token{
  954.  
  955.                 // use while loop instead of if block because it
  956.                 // is possible we don't want the first one we find
  957.                 //
  958.                 $pos $soft_wrap 1// -1 is corrected by +1 on next line
  959.                 while ($pos strpos($hard_wrapped_line$token$pos 1))
  960.                 {
  961.  
  962.                     $new_fold substr($header0$pos);
  963.  
  964.                     // make sure proposed fold doesn't create a blank line
  965.                     //
  966.                     if (!trim($new_fold)) continue;
  967.  
  968.                     // with whitespace breaks, we fold BEFORE the token
  969.                     //
  970.                     $folded_header .= $new_fold $CRLF $indent;
  971.                     $header substr($header$pos);
  972.  
  973.                     // ready for next outter while() iteration
  974.                     //
  975.                     continue 3;
  976.  
  977.                 }
  978.  
  979.             }
  980.  
  981.             // we were still unable to find a soft wrapping point within
  982.             // both the soft and hard wrap limits, so if the length of
  983.             // what is left is no more than the hard wrap limit, we'll
  984.             // simply take the whole thing
  985.             //
  986.             if (strlen($header<= $hard_wrap{
  987.  
  988.                 // if the header has been folded at least once before now,
  989.                 // let's see if we can add the remaining chunk to the last
  990.                 // fold (this is mainly just aesthetic)
  991.                 //
  992.                 if ($pull_last_line_up_if_second_to_last_is_already_over_soft_limit
  993.                  && strlen($folded_header)
  994.                  // last fold is conveniently in $new_fold
  995.                  && strlen($new_foldstrlen($header<= $hard_wrap{
  996.                     // $last_fold = substr(substr($folded_header, 0, -(strlen($CRLF) + strlen($indent))), 
  997.                     // remove CRLF and indentation and paste the rest of the header on
  998.                     $folded_header substr($folded_header0-(strlen($CRLFstrlen($indent))) $header;
  999.                     $header '';
  1000.                 }
  1001.  
  1002.                 break;
  1003.             }
  1004.  
  1005.             // otherwise, we can't quit yet - look for a "hard" token
  1006.             // as close to the end of the hard wrap limit as possible
  1007.             //
  1008.             foreach ($hard_break_tokens as $token{
  1009.  
  1010.                 // note that this if statement also fails when $pos === 0,
  1011.                 // which is intended, since blank lines are not allowed
  1012.                 //
  1013.                 if ($pos strrpos($hard_wrapped_line$token))
  1014.                 {
  1015.  
  1016.                     // if we found a "=" token, we must determine whether,
  1017.                     // if it is part of an encoded word, it is the beginning
  1018.                     // or middle of one, where we need to readjust $pos a bit
  1019.                     //
  1020.                     if ($token == '='{
  1021.  
  1022.                         // if we found the beginning of an encoded word,
  1023.                         // we want to break BEFORE the token
  1024.                         //
  1025.                         if (preg_match('/^(=\?([^?]*)\?(Q|B)\?([^?]*)\?=)/Ui',
  1026.                                        substr($header$pos))) {
  1027.                             $pos--;
  1028.                         }
  1029.  
  1030.                         // check if we found this token in the *middle*
  1031.                         // of an encoded word, in which case we have to
  1032.                         // ignore it, pushing back to the token that
  1033.                         // starts the encoded word instead
  1034.                         //
  1035.                         // of course, this is only possible if there is
  1036.                         // more content after the next hard wrap
  1037.                         //
  1038.                         // then look for the end of an encoded word in
  1039.                         // the next part (past the next hard wrap)
  1040.                         //
  1041.                         // then see if it is in fact part of a legitimate
  1042.                         // encoded word
  1043.                         //
  1044.                         else if (strlen($header$hard_wrap
  1045.                          && ($end_pos strpos(substr($header$hard_wrap)'?=')) !== FALSE
  1046.                          && preg_match('/(=\?([^?]*)\?(Q|B)\?([^?]*)\?=)$/Ui',
  1047.                                        substr($header0$hard_wrap $end_pos 2),
  1048.                                        $matches)) {
  1049.  
  1050.                             $pos $hard_wrap $end_pos strlen($matches[1]1;
  1051.  
  1052.                         }
  1053.  
  1054.                     }
  1055.  
  1056.                     // $pos could have been changed; make sure it's
  1057.                     // not at the beginning of the line, as blank
  1058.                     // lines are not allowed
  1059.                     //
  1060.                     if ($pos === 0continue;
  1061.  
  1062.                     // we are dealing with a simple token break...
  1063.                     //
  1064.                     // for non-whitespace breaks, we fold AFTER the token
  1065.                     // and add a space after the fold if not immediately
  1066.                     // followed by a whitespace character in the next part
  1067.                     //
  1068.                     // $new_fold is used above, it's assumed we update it upon every fold action
  1069.                     $new_fold substr($header0$pos 1);
  1070.                     $folded_header .= $new_fold $CRLF;
  1071.  
  1072.                     // don't go beyond end of $header, though
  1073.                     //
  1074.                     if (strlen($header$pos 1{
  1075.                         $header substr($header$pos 1);
  1076.                         if (!in_array($header{0}$whitespace))
  1077.                             $header ' ' $indent $header;
  1078.                     else {
  1079.                         $header '';
  1080.                     }
  1081.  
  1082.                     // ready for next while() iteration
  1083.                     //
  1084.                     continue 2;
  1085.  
  1086.                 }
  1087.  
  1088.             }
  1089.  
  1090.             // finally, we just couldn't find anything to fold on, so we
  1091.             // have to just cut it off at the hard limit
  1092.             //
  1093.             // $new_fold is used above, it's assumed we update it upon every fold action
  1094.             $new_fold $hard_wrapped_line;
  1095.             $folded_header .= $new_fold $CRLF;
  1096.  
  1097.             // is there more?
  1098.             //
  1099.             if (strlen($headerstrlen($hard_wrapped_line)) {
  1100.                 $header substr($headerstrlen($hard_wrapped_line));
  1101.                 if (!in_array($header{0}$whitespace))
  1102.                     $header ' ' $indent $header;
  1103.             else {
  1104.                 $header '';
  1105.             }
  1106.  
  1107.         }
  1108.  
  1109.  
  1110.         // add any left-overs
  1111.         //
  1112.         $folded_header .= $header;
  1113.  
  1114.  
  1115.         // make sure it ends with a CRLF
  1116.         //
  1117.         if (substr($folded_header-2!= $CRLF$folded_header .= $CRLF;
  1118.  
  1119.  
  1120.         return $folded_header;
  1121.     }
  1122.  
  1123.     /**
  1124.      * function mimeBoundary - calculates the mime boundary to use
  1125.      *
  1126.      * This function will generate a random mime boundary base part
  1127.      * for the message if the boundary has not already been set.
  1128.      *
  1129.      * @return string $mimeBoundaryString random mime boundary string
  1130.      */
  1131.     function mimeBoundary ({
  1132.         static $mimeBoundaryString;
  1133.  
  1134.         if !isset$mimeBoundaryString ||
  1135.             $mimeBoundaryString == ''{
  1136.             $mimeBoundaryString '----=_' date'YmdHis' '_' .
  1137.             mt_rand1000099999 );
  1138.         }
  1139.         return $mimeBoundaryString;
  1140.     }
  1141.  
  1142.     /**
  1143.      * function timezone - Time offset for correct timezone
  1144.      *
  1145.      * @return string $result with timezone and offset
  1146.      */
  1147.     function timezone ({
  1148.         global $invert_time$show_timezone_name;
  1149.  
  1150.         $diff_second date('Z');
  1151.         if ($invert_time{
  1152.             $diff_second = - $diff_second;
  1153.         }
  1154.         if ($diff_second 0{
  1155.             $sign '+';
  1156.         else {
  1157.             $sign '-';
  1158.         }
  1159.         $diff_second abs($diff_second);
  1160.         $diff_hour floor ($diff_second 3600);
  1161.         $diff_minute floor (($diff_second-3600*$diff_hour60);
  1162.  
  1163.         // If an administrator wants to add the timezone name to the
  1164.         // end of the date header, they can set $show_timezone_name
  1165.         // to boolean TRUE in config/config_local.php, but that is
  1166.         // NOT RFC-822 compliant (see section 5.1).  Moreover, some
  1167.         // Windows users reported that strftime('%Z') was returning
  1168.         // the full zone name (not the abbreviation) which in some
  1169.         // cases included 8-bit characters (not allowed as is in headers).
  1170.         // The PHP manual actually does NOT promise what %Z will return
  1171.         // for strftime!:  "The time zone offset/abbreviation option NOT
  1172.         // given by %z (depends on operating system)"
  1173.         //
  1174.         if ($show_timezone_name{
  1175.             $zonename '('.strftime('%Z').')';
  1176.             $result sprintf ("%s%02d%02d %s"$sign$diff_hour$diff_minute$zonename);
  1177.         else {
  1178.             $result sprintf ("%s%02d%02d"$sign$diff_hour$diff_minute);
  1179.         }
  1180.         return ($result);
  1181.     }
  1182.  
  1183.     /**
  1184.      * function calculate_references - calculate correct References string
  1185.      * Adds the current message ID, and makes sure it doesn't grow forever,
  1186.      * to that extent it drops message-ID's in a smart way until the string
  1187.      * length is under the recommended value of 1000 ("References: <986>\r\n").
  1188.      * It always keeps the first and the last three ID's.
  1189.      *
  1190.      * @param   Rfc822Header $hdr    message header to calculate from
  1191.      *
  1192.      * @return  string       $refer  concatenated and trimmed References string
  1193.      */
  1194.     function calculate_references($hdr{
  1195.         $aReferences preg_split('/\s+/'$hdr->references);
  1196.         $message_id $hdr->message_id;
  1197.         $in_reply_to $hdr->in_reply_to;
  1198.  
  1199.         // if References already exists, add the current message ID at the end.
  1200.         // no References exists; if we know a IRT, add that aswell
  1201.         if (count($aReferences== && $in_reply_to{
  1202.             $aReferences[$in_reply_to;
  1203.         }
  1204.         $aReferences[$message_id;
  1205.  
  1206.         // sanitize the array: trim whitespace, remove dupes
  1207.         array_walk($aReferences'sq_trim_value');
  1208.         $aReferences array_unique($aReferences);
  1209.  
  1210.         while count($aReferences&& strlen(implode(' '$aReferences)) >= 986 {
  1211.             $aReferences array_merge(array_slice($aReferences,0,1),array_slice($aReferences,2));
  1212.         }
  1213.         return implode(' '$aReferences);
  1214.     }
  1215.  
  1216.     /**
  1217.      * Converts ip address to hexadecimal string
  1218.      *
  1219.      * Function is used to convert ipv4 and ipv6 addresses to hex strings.
  1220.      * It removes all delimiter symbols from ip addresses, converts decimal
  1221.      * ipv4 numbers to hex and pads strings in order to present full length
  1222.      * address. ipv4 addresses are represented as 8 byte strings, ipv6 addresses
  1223.      * are represented as 32 byte string.
  1224.      *
  1225.      * If function fails to detect address format, it returns unprocessed string.
  1226.      * @param string $string ip address string
  1227.      * @return string processed ip address string
  1228.      * @since 1.5.1 and 1.4.5
  1229.      */
  1230.     function ip2hex($string{
  1231.         if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/",$string,$match)) {
  1232.             // ipv4 address
  1233.             $ret str_pad(dechex($match[1]),2,'0',STR_PAD_LEFT)
  1234.                 . str_pad(dechex($match[2]),2,'0',STR_PAD_LEFT)
  1235.                 . str_pad(dechex($match[3]),2,'0',STR_PAD_LEFT)
  1236.                 . str_pad(dechex($match[4]),2,'0',STR_PAD_LEFT);
  1237.         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)) {
  1238.             // full ipv6 address
  1239.             $ret str_pad($match[1],4,'0',STR_PAD_LEFT)
  1240.                 . str_pad($match[2],4,'0',STR_PAD_LEFT)
  1241.                 . str_pad($match[3],4,'0',STR_PAD_LEFT)
  1242.                 . str_pad($match[4],4,'0',STR_PAD_LEFT)
  1243.                 . str_pad($match[5],4,'0',STR_PAD_LEFT)
  1244.                 . str_pad($match[6],4,'0',STR_PAD_LEFT)
  1245.                 . str_pad($match[7],4,'0',STR_PAD_LEFT)
  1246.                 . str_pad($match[8],4,'0',STR_PAD_LEFT);
  1247.         elseif (preg_match("/^\:\:([0-9a-h\:]+)$/i",$string,$match)) {
  1248.             // short ipv6 with all starting symbols nulled
  1249.             $aAddr=explode(':',$match[1]);
  1250.             $ret='';
  1251.             foreach ($aAddr as $addr{
  1252.                 $ret.=str_pad($addr,4,'0',STR_PAD_LEFT);
  1253.             }
  1254.             $ret=str_pad($ret,32,'0',STR_PAD_LEFT);
  1255.         elseif (preg_match("/^([0-9a-h\:]+)::([0-9a-h\:]+)$/i",$string,$match)) {
  1256.             // short ipv6 with middle part nulled
  1257.             $aStart=explode(':',$match[1]);
  1258.             $sStart='';
  1259.             foreach($aStart as $addr{
  1260.                 $sStart.=str_pad($addr,4,'0',STR_PAD_LEFT);
  1261.             }
  1262.             $aEnd explode(':',$match[2]);
  1263.             $sEnd='';
  1264.             foreach($aEnd as $addr{
  1265.                 $sEnd.=str_pad($addr,4,'0',STR_PAD_LEFT);
  1266.             }
  1267.             $ret $sStart
  1268.                 . str_pad('',(32 strlen($sStart $sEnd)),'0',STR_PAD_LEFT)
  1269.                 . $sEnd;
  1270.         else {
  1271.             // unknown addressing
  1272.             $ret $string;
  1273.         }
  1274.         return $ret;
  1275.     }
  1276. }

Documentation generated on Mon, 13 Jan 2020 04:22:21 +0100 by phpDocumentor 1.4.3