class.t3lib_htmlmail.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2010 Kasper Skaarhoj (kasperYYYY@typo3.com)
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 /**
00028  * HTML mail class
00029  *
00030  * $Id: class.t3lib_htmlmail.php 8156 2010-07-11 12:42:05Z psychomieze $
00031  *
00032  * @author  Kasper Skaarhoj <kasperYYYY@typo3.com>
00033  */
00034 /**
00035  * [CLASS/FUNCTION INDEX of SCRIPT]
00036  *
00037  *
00038  *
00039  *  193: class t3lib_htmlmail
00040  *  261:     function t3lib_htmlmail ()
00041  *  268:     function start ()
00042  *  305:     function useQuotedPrintable()
00043  *  315:     function useBase64()
00044  *  326:     function use8Bit()
00045  *  338:     function encodeMsg($content)
00046  *  348:     function addPlain ($content)
00047  *  360:     function addAttachment($file)
00048  *  378:     function addHTML ($file)
00049  *  401:     function extractHtmlInit($html,$url)
00050  *  412:     function send($recipient)
00051  *
00052  *              SECTION: Main functions
00053  *  441:     function setHeaders()
00054  *  500:     function setRecipient ($recip)
00055  *  518:     function getHTMLContentType()
00056  *  527:     function setContent()
00057  *  554:     function constructMixed ($boundary)
00058  *  593:     function constructHTML ($boundary)
00059  *  617:     function constructAlternative($boundary)
00060  *  638:     function constructHTML_media ($boundary)
00061  *  691:     function sendTheMail ()
00062  *  757:     function getBoundary()
00063  *  769:     function setPlain ($content)
00064  *  780:     function setHtml ($content)
00065  *  791:     function add_header($header)
00066  *  812:     function add_message($string)
00067  *  823:     function getContent($type)
00068  *  832:     function preview()
00069  *
00070  *              SECTION: Functions for acquiring attachments, HTML, analyzing and so on  **
00071  *  860:     function fetchHTML($file)
00072  *  878:     function fetchHTMLMedia()
00073  *  899:     function extractMediaLinks()
00074  *  976:     function extractHyperLinks()
00075  * 1025:     function extractFramesInfo()
00076  * 1051:     function substMediaNamesInHTML($absolute)
00077  * 1078:     function substHREFsInHTML()
00078  * 1106:     function substHTTPurlsInPlainText($content)
00079  * 1142:     function fixRollOvers()
00080  *
00081  *              SECTION: File and URL-functions
00082  * 1189:     function makeBase64($inputstr)
00083  * 1200:     function getExtendedURL($url)
00084  * 1222:     function addUserPass($url)
00085  * 1238:     function getURL($url)
00086  * 1250:     function getStrippedURL($url)
00087  * 1271:     function getMimeType($url)
00088  * 1300:     function absRef($ref)
00089  * 1320:     function split_fileref($fileref)
00090  * 1347:     function extParseUrl($path)
00091  * 1362:     function tag_regex($tagArray)
00092  * 1384:     function get_tag_attributes($tag)
00093  * 1426:     function quoted_printable($string)
00094  * 1437:     function convertName($name)
00095  *
00096  * TOTAL FUNCTIONS: 49
00097  * (This index is automatically created/updated by the extension "extdeveval")
00098  *
00099  */
00100 /**
00101  * NOTES on MIME mail structures:
00102  *
00103  * Plain + HTML
00104  *  multipart/alternative   (text, html)
00105  *  multipart/alternative   (text, html)
00106  *
00107  * Plain + HTML + image
00108  *  multipart/related (m/a, cids)
00109  *      multipart/alternative (text, html)
00110  *
00111  *  multipart/related  (m/a, cids)
00112  *      multipart/alternative   (text, html)
00113  *
00114  * plain + attachment
00115  *  multipart/mixed
00116  *
00117  * HTML + Attachment:
00118  *  multipart/mixed     (text/html , attachments)
00119  *
00120  * Plain + HTML + Attachments:
00121  *  multipart/mixed     (m/a, attachments)
00122  *      multipart/alternative   (text, html)
00123  *
00124  * Plain + HTML + image + attachment
00125  *
00126  *      Outlook expr.
00127  *  multipart/mixed (m/r, attachments)
00128  *      multipart/related  (m/a, cids)
00129  *          multipart/alternative   (text, html)
00130  *
00131  *
00132  *
00133  * FROM RFC 1521:
00134  *
00135  * 5.1 Quoted-Printable Content-Transfer-Encoding
00136  * The Quoted-Printable encoding is intended to represent data that largely consists of octets that correspond to printable characters in the ASCII character set. It encodes the data in such a way that the resulting octets are unlikely to be modified by mail transport. If the data being encoded are mostly ASCII text, the encoded form of the data remains largely recognizable by humans. A body which is entirely ASCII may also be encoded in Quoted-Printable to ensure the integrity of the data should the message pass through a character- translating, and/or line-wrapping gateway.
00137  *
00138  * In this encoding, octets are to be represented as determined by the following rules:
00139  * Rule #1: (General 8-bit representation) Any octet, except those indicating a line break according to the newline convention of the canonical (standard) form of the data being encoded, may be represented by an "=" followed by a two digit hexadecimal representation of the octet's value. The digits of the hexadecimal alphabet, for this purpose, are "0123456789ABCDEF". Uppercase letters must be used when sending hexadecimal data, though a robust implementation may choose to recognize lowercase letters on receipt. Thus, for example, the value 12 (ASCII form feed) can be represented by "=0C", and the value 61 (ASCII EQUAL SIGN) can be represented by "=3D". Except when the following rules allow an alternative encoding, this rule is mandatory.
00140  * Rule #2: (Literal representation) Octets with decimal values of 33 through 60 inclusive, and 62 through 126, inclusive, MAY be represented as the ASCII characters which correspond to those octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN through TILDE, respectively).
00141  * Rule #3: (White Space): Octets with values of 9 and 32 MAY be represented as ASCII TAB (HT) and SPACE characters, respectively, but MUST NOT be so represented at the end of an encoded line. Any TAB (HT) or SPACE characters on an encoded line MUST thus be followed on that line by a printable character. In particular, an
00142  * "=" at the end of an encoded line, indicating a soft line break (see rule #5) may follow one or more TAB (HT) or SPACE characters. It follows that an octet with value 9 or 32 appearing at the end of an encoded line must be represented according to Rule #1. This rule is necessary because some MTAs (Message Transport Agents, programs which transport messages from one user to another, or perform a part of such transfers) are known to pad lines of text with SPACEs, and others are known to remove "white space" characters from the end of a line. Therefore, when decoding a Quoted-Printable body, any trailing white space on a line must be deleted, as it will necessarily have been added by intermediate transport agents.
00143  * Rule #4 (Line Breaks): A line break in a text body, independent of what its representation is following the canonical representation of the data being encoded, must be represented by a (RFC 822) line break, which is a CRLF sequence, in the Quoted-Printable encoding. Since the canonical representation of types other than text do not generally include the representation of line breaks, no hard line breaks (i.e. line breaks that are intended to be meaningful and to be displayed to the user) should occur in the quoted-printable encoding of such types. Of course, occurrences of "=0D", "=0A", "0A=0D" and "=0D=0A" will eventually be encountered. In general, however, base64 is preferred over quoted-printable for binary data.
00144  * Note that many implementations may elect to encode the local representation of various content types directly, as described in Appendix G. In particular, this may apply to plain text material on systems that use newline conventions other than CRLF delimiters. Such an implementation is permissible, but the generation of line breaks must be generalized to account for the case where alternate representations of newline sequences are used.
00145  * Rule #5 (Soft Line Breaks): The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, 'soft' line breaks must be used. An equal sign as the last character on a encoded line indicates such a non-significant ('soft') line break in the encoded text. Thus if the "raw" form of the line is a single unencoded line that says:
00146  * Now's the time for all folk to come to the aid of their country.
00147  *
00148  * This can be represented, in the Quoted-Printable encoding, as
00149  *
00150  * Now's the time =
00151  * for all folk to come=
00152  * to the aid of their country.
00153  *
00154  * This provides a mechanism with which long lines are encoded in such a way as to be restored by the user agent. The 76 character limit does not count the trailing CRLF, but counts all other characters, including any equal signs.
00155  * Since the hyphen character ("-") is represented as itself in the Quoted-Printable encoding, care must be taken, when encapsulating a quoted-printable encoded body in a multipart entity, to ensure that the encapsulation boundary does not appear anywhere in the encoded body. (A good strategy is to choose a boundary that includes a character sequence such as "=_" which can never appear in a quoted- printable body. See the definition of multipart messages later in this document.)
00156  * NOTE: The quoted-printable encoding represents something of a compromise between readability and reliability in transport. Bodies encoded with the quoted-printable encoding will work reliably over most mail gateways, but may not work perfectly over a few gateways, notably those involving translation into EBCDIC. (In theory, an EBCDIC gateway could decode a quoted-printable body and re-encode it using base64, but such gateways do not yet exist.) A higher level of confidence is offered by the base64 Content-Transfer-Encoding. A way to get reasonably reliable transport through EBCDIC gateways is to also quote the ASCII characters
00157  * !"#$@[\]^`{|}~
00158  * according to rule #1. See Appendix B for more information.
00159  * Because quoted-printable data is generally assumed to be line- oriented, it is to be expected that the representation of the breaks between the lines of quoted printable data may be altered in transport, in the same manner that plain text mail has always been altered in Internet mail when passing between systems with differing newline conventions. If such alterations are likely to constitute a corruption of the data, it is probably more sensible to use the base64 encoding rather than the quoted-printable encoding.
00160  * WARNING TO IMPLEMENTORS: If binary data are encoded in quoted- printable, care must be taken to encode CR and LF characters as "=0D" and "=0A", respectively. In particular, a CRLF sequence in binary data should be encoded as "=0D=0A". Otherwise, if CRLF were represented as a hard line break, it might be incorrectly decoded on
00161  * platforms with different line break conventions.
00162  * For formalists, the syntax of quoted-printable data is described by the following grammar:
00163  *
00164  *    quoted-printable := ([*(ptext / SPACE / TAB) ptext] ["="] CRLF)
00165  *         ; Maximum line length of 76 characters excluding CRLF
00166  *
00167  *    ptext := octet /<any ASCII character except "=", SPACE, or TAB>
00168  *         ; characters not listed as "mail-safe" in Appendix B
00169  *         ; are also not recommended.
00170  *
00171  *    octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F")
00172  *         ; octet must be used for characters > 127, =, SPACE, or TAB,
00173  *         ; and is recommended for any characters not listed in
00174  *         ; Appendix B as "mail-safe".
00175  */
00176 /**
00177  * HTML mail class
00178  *
00179  * @author  Kasper Skaarhoj <kasperYYYY@typo3.com>
00180  * @package TYPO3
00181  * @subpackage  t3lib
00182  */
00183 class t3lib_htmlmail {
00184         // Headerinfo:
00185     var $recipient      = '';
00186     var $recipient_copy = '';   // This recipient (or list of...) will also receive the mail. Regard it as a copy.
00187     var $recipient_blindcopy = ''; // This recipient (or list of...) will also receive the mail as a blind copy. Regard it as a copy.
00188     var $subject        = '';
00189     var $from_email     = '';
00190     var $from_name      = '';
00191     var $replyto_email  = '';
00192     var $replyto_name   = '';
00193     var $organisation   = '';
00194     var $priority       = 3;    // 1 = highest, 5 = lowest, 3 = normal
00195     var $mailer         = '';   // X-mailer, set to TYPO3 Major.Minor in constructor
00196     var $alt_base64     = 0;
00197     var $alt_8bit       = 0;
00198     var $jumperURL_prefix   = '';       // This is a prefix that will be added to all links in the mail. Example: 'http://www.mydomain.com/jump?userid=###FIELD_uid###&url='. if used, anything after url= is urlencoded.
00199     var $jumperURL_useId    = 0;            // If set, then the array-key of the urls are inserted instead of the url itself. Smart in order to reduce link-length
00200     var $mediaList      = '';               // If set, this is a list of the media-files (index-keys to the array) that should be represented in the html-mail
00201     var $http_password  = '';
00202     var $http_username  = '';
00203     var $postfix_version1   = false;
00204 
00205     // Internal
00206     /*
00207     This is how the $theParts-array is normally looking
00208     var $theParts = array(
00209         'plain' => array(
00210             'content' => ''
00211         ),
00212         'html' => array(
00213             'content' => '',
00214             'path' => '',
00215             'media' => array(),
00216             'hrefs' => array()
00217         ),
00218         'attach' => array()
00219     );
00220     */
00221     var $theParts = array();
00222 
00223     var $messageid = '';
00224     var $returnPath = '';
00225     var $Xid = '';
00226     var $dontEncodeHeader = false;      // If set, the header will not be encoded
00227 
00228     var $headers = '';
00229     var $message = '';
00230     var $part = 0;
00231     var $image_fullpath_list = '';
00232     var $href_fullpath_list = '';
00233 
00234     var $plain_text_header = '';
00235     var $html_text_header = '';
00236     var $charset = '';
00237     var $defaultCharset = 'iso-8859-1';
00238 
00239 
00240 
00241     /**
00242      * Constructor. If the configuration variable forceReturnPath is set,
00243      * calls to mail will be called with a 5th parameter.
00244      * See function sendTheMail for more info
00245      *
00246      * @return  void
00247      */
00248     public function t3lib_htmlmail() {
00249         $this->forceReturnPath = $GLOBALS['TYPO3_CONF_VARS']['SYS']['forceReturnPath'];
00250 
00251         $this->mailer = 'TYPO3 '.TYPO3_version;
00252     }
00253 
00254 
00255     /**
00256      * start action that sets the message ID and the charset
00257      *
00258      * @return  void
00259      */
00260     public function start() {
00261         global $TYPO3_CONF_VARS;
00262 
00263             // Sets the message id
00264         $host = t3lib_div::getHostname();
00265         if (!$host || $host == '127.0.0.1' || $host == 'localhost' || $host == 'localhost.localdomain') {
00266             $host = ($TYPO3_CONF_VARS['SYS']['sitename'] ? preg_replace('/[^A-Za-z0-9_\-]/', '_', $TYPO3_CONF_VARS['SYS']['sitename']) : 'localhost') . '.TYPO3';
00267         }
00268         $this->messageid = md5(microtime()) . '@' . $host;
00269 
00270 
00271             // Default line break for Unix systems.
00272         $this->linebreak = LF;
00273             // Line break for Windows. This is needed because PHP on Windows systems
00274             // send mails via SMTP instead of using sendmail, and thus the linebreak needs to be \r\n.
00275         if (TYPO3_OS == 'WIN') {
00276             $this->linebreak = CRLF;
00277         }
00278 
00279             // Sets the Charset
00280         if (!$this->charset) {
00281             if (is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->renderCharset) {
00282                 $this->charset = $GLOBALS['TSFE']->renderCharset;
00283             } elseif (is_object($GLOBALS['LANG']) && $GLOBALS['LANG']->charSet) {
00284                 $this->charset = $GLOBALS['LANG']->charSet;
00285             } elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) {
00286                 $this->charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
00287             } else  {
00288                 $this->charset = $this->defaultCharset;
00289             }
00290         }
00291 
00292             // Use quoted-printable headers by default
00293         $this->useQuotedPrintable();
00294     }
00295 
00296 
00297     /**
00298      * sets the header of both Plain Text and HTML mails to quoted printable
00299      *
00300      * @return  void
00301      */
00302     public function useQuotedPrintable() {
00303         $this->plain_text_header = 'Content-Type: text/plain; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: quoted-printable';
00304         $this->html_text_header = 'Content-Type: text/html; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: quoted-printable';
00305     }
00306 
00307     /**
00308      * sets the encoding headers to base64 for both the Plain Text and HTML mail
00309      *
00310      * @return  void
00311      */
00312     public function useBase64() {
00313         $this->plain_text_header = 'Content-Type: text/plain; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: base64';
00314         $this->html_text_header = 'Content-Type: text/html; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: base64';
00315         $this->alt_base64 = 1;
00316     }
00317 
00318 
00319     /**
00320      * sets the encoding to 8bit and the current charset of both the Plain Text and the HTML mail
00321      *
00322      * @return  void
00323      */
00324     public function use8Bit() {
00325         $this->plain_text_header = 'Content-Type: text/plain; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: 8bit';
00326         $this->html_text_header = 'Content-Type: text/html; charset='.$this->charset.$this->linebreak.'Content-Transfer-Encoding: 8bit';
00327         $this->alt_8bit = 1;
00328     }
00329 
00330 
00331     /**
00332      * Encodes the message content according to the options "alt_base64" and "alt_8bit" (no need to encode here)
00333      * or to "quoted_printable" if no option is set.
00334      *
00335      * @param   string      $content the content that will be encoded
00336      * @return  string      the encoded content
00337      */
00338     public function encodeMsg($content) {
00339         if ($this->alt_base64) {
00340             return $this->makeBase64($content);
00341         } elseif ($this->alt_8bit) {
00342             return $content;
00343         } else  {
00344             return t3lib_div::quoted_printable($content);
00345         }
00346     }
00347 
00348 
00349     /**
00350      * Adds plain-text, replaces the HTTP urls in the plain text and then encodes it
00351      *
00352      * @param   string      $content that will be added
00353      * @return  void
00354      */
00355     public function addPlain($content) {
00356         $content = $this->substHTTPurlsInPlainText($content);
00357         $this->setPlain($this->encodeMsg($content));
00358     }
00359 
00360 
00361     /**
00362      * Adds an attachment to the mail
00363      *
00364      * @param   string      $file: the filename to add
00365      * @return  boolean     whether the attachment was added or not
00366      */
00367     public function addAttachment($file) {
00368             // Fetching the content and the mime-type
00369         $fileInfo = $this->getExtendedURL($file);
00370         if ($fileInfo) {
00371             if (!$fileInfo['content_type']) {
00372                 $fileInfo['content_type'] = 'application/octet-stream';
00373             }
00374             $temp = $this->split_fileref($file);
00375             if ($temp['file']) {
00376                 $fileInfo['filename'] = $temp['file'];
00377             } elseif (strpos(' '.$fileInfo['content_type'], 'htm')) {
00378                 $fileInfo['filename'] = 'index.html';
00379             } else  {
00380                 $fileInfo['filename'] = 'unknown';
00381             }
00382             $this->theParts['attach'][] = $fileInfo;
00383             return true;
00384         }
00385         return false;
00386     }
00387 
00388 
00389     /**
00390      * Adds HTML and media, encodes it from a URL or file
00391      *
00392      * @param   string      $file: the filename to add
00393      * @return  boolean     whether the attachment was added or not
00394      */
00395     public function addHTML($file) {
00396         $status = $this->fetchHTML($file);
00397         if (!$status) {
00398             return false;
00399         }
00400         if ($this->extractFramesInfo()) {
00401             return 'Document was a frameset. Stopped';
00402         }
00403         $this->extractMediaLinks();
00404         $this->extractHyperLinks();
00405         $this->fetchHTMLMedia();
00406         $this->substMediaNamesInHTML(0);    // 0 = relative
00407         $this->substHREFsInHTML();
00408         $this->setHtml($this->encodeMsg($this->theParts['html']['content']));
00409     }
00410 
00411 
00412     /**
00413      * Extract HTML-parts, used externally
00414      *
00415      * @param   string      $html: will be added to the html "content" part
00416      * @param   string      $url: will be added to the html "path" part
00417      * @return  void
00418      */
00419     public function extractHtmlInit($html,$url) {
00420         $this->theParts['html']['content'] = $html;
00421         $this->theParts['html']['path'] = $url;
00422     }
00423 
00424 
00425     /**
00426      * Assembles the message by headers and content and finally send it to the provided recipient.
00427      *
00428      * @param   string      $recipient: The recipient the message should be delivered to (if blank, $this->recipient will be used instead)
00429      * @return  boolean     Returns whether the mail was sent (successfully accepted for delivery)
00430      */
00431     public function send($recipient) {
00432         if ($recipient) {
00433             $this->recipient = $recipient;
00434         }
00435         $this->setHeaders();
00436         $this->setContent();
00437         $mailWasSent = $this->sendTheMail();
00438         return $mailWasSent;
00439     }
00440 
00441 
00442 
00443 
00444 
00445 
00446 
00447 
00448 
00449 
00450 
00451 
00452 
00453     /*****************************************
00454      *
00455      * Main functions
00456      *
00457      *****************************************/
00458 
00459     /**
00460      * Clears the header-string and sets the headers based on object-vars.
00461      *
00462      * @return  void
00463      */
00464     public function setHeaders() {
00465         $this->headers = '';
00466             // Message_id
00467         $this->add_header('Message-ID: <'.$this->messageid.'>');
00468             // Return path
00469         if ($this->returnPath) {
00470             $this->add_header('Return-Path: '.$this->returnPath);
00471             $this->add_header('Errors-To: '.$this->returnPath);
00472         }
00473             // X-id
00474         if ($this->Xid) {
00475             $this->add_header('X-Typo3MID: '.$this->Xid);
00476         }
00477 
00478             // From
00479         if ($this->from_email) {
00480             if ($this->from_name && !t3lib_div::isBrokenEmailEnvironment()) {
00481                 $this->add_header('From: '.$this->from_name.' <'.$this->from_email.'>');
00482             } else {
00483                 $this->add_header('From: '.$this->from_email);
00484             }
00485         }
00486 
00487             // Cc
00488         if ($this->recipient_copy) {
00489             $this->add_header('Cc: ' . $this->recipient_copy);
00490         }
00491 
00492             // Bcc
00493         if ($this->recipient_blindcopy) {
00494             $this->add_header('Bcc: ' . $this->recipient_blindcopy);
00495         }
00496 
00497             // Reply
00498         if ($this->replyto_email) {
00499             if ($this->replyto_name) {
00500                 $this->add_header('Reply-To: '.$this->replyto_name.' <'.$this->replyto_email.'>');
00501             } else {
00502                 $this->add_header('Reply-To: '.$this->replyto_email);
00503             }
00504         }
00505             // Organization, using american english spelling (organization / organisation) as defined in RFC 1036 / 2076
00506         if ($this->organisation) {
00507             $this->add_header('Organization: ' . $this->organisation);
00508         }
00509             // mailer
00510         if ($this->mailer) {
00511             $this->add_header('X-Mailer: '.$this->mailer);
00512         }
00513             // priority
00514         if ($this->priority) {
00515             $this->add_header('X-Priority: '.$this->priority);
00516         }
00517         $this->add_header('Mime-Version: 1.0');
00518 
00519         if (!$this->dontEncodeHeader) {
00520             $enc = $this->alt_base64 ? 'base64' : 'quoted_printable';   // Header must be ASCII, therefore only base64 or quoted_printable are allowed!
00521                 // Quote recipient and subject
00522             $this->recipient = t3lib_div::encodeHeader($this->recipient,$enc,$this->charset);
00523             $this->subject = t3lib_div::encodeHeader($this->subject,$enc,$this->charset);
00524         }
00525     }
00526 
00527 
00528     /**
00529      * Sets the recipient(s). If you supply a string, you set one recipient.
00530      * If you supply an array, every value is added as a recipient.
00531      *
00532      * @param   mixed       $recipient: the recipient(s) to set
00533      * @return  void
00534      */
00535     public function setRecipient($recipient) {
00536         $this->recipient = (is_array($recipient) ? implode(',', $recipient) : $recipient);
00537     }
00538 
00539 
00540     /**
00541      * Returns the content type based on whether the mail has media / attachments or no
00542      *
00543      * @return  string      the content type
00544      */
00545     public function getHTMLContentType() {
00546         return (count($this->theParts['html']['media']) ? 'multipart/related' : 'multipart/alternative');
00547     }
00548 
00549 
00550     /**
00551      * Begins building the message-body
00552      *
00553      * @return  void
00554      */
00555     public function setContent() {
00556         $this->message = '';
00557         $boundary = $this->getBoundary();
00558 
00559             // Setting up headers
00560         if (count($this->theParts['attach'])) {
00561             // Generate (plain/HTML) / attachments
00562             $this->add_header('Content-Type: multipart/mixed;');
00563             $this->add_header(' boundary="' . $boundary . '"');
00564             $this->add_message('This is a multi-part message in MIME format.' . LF);
00565             $this->constructMixed($boundary);
00566         } elseif ($this->theParts['html']['content']) {
00567             // Generate plain/HTML mail
00568             $this->add_header('Content-Type: ' . $this->getHTMLContentType() . ';');
00569             $this->add_header(' boundary="' . $boundary . '"');
00570             $this->add_message('This is a multi-part message in MIME format.' . LF);
00571             $this->constructHTML($boundary);
00572         } else {
00573             // Generate plain only
00574             $this->add_header($this->plain_text_header);
00575             $this->add_message($this->getContent('plain'));
00576         }
00577     }
00578 
00579 
00580     /**
00581      * This functions combines the plain / HTML content with the attachments
00582      *
00583      * @param   string      $boundary: the mail boundary
00584      * @return  void
00585      */
00586     public function constructMixed($boundary) {
00587         $this->add_message('--' . $boundary);
00588 
00589         if ($this->theParts['html']['content']) {
00590             // HTML and plain is added
00591             $newBoundary = $this->getBoundary();
00592             $this->add_message('Content-Type: '.$this->getHTMLContentType() . ';');
00593             $this->add_message(' boundary="' . $newBoundary . '"');
00594             $this->add_message('');
00595             $this->constructHTML($newBoundary);
00596         } else {
00597             // Purely plain
00598             $this->add_message($this->plain_text_header);
00599             $this->add_message('');
00600             $this->add_message($this->getContent('plain'));
00601         }
00602         // attachments are added
00603         if (is_array($this->theParts['attach'])) {
00604             foreach ($this->theParts['attach'] as $media) {
00605                 $this->add_message('--' . $boundary);
00606                 $this->add_message('Content-Type: ' . $media['content_type'] . ';');
00607                 $this->add_message(' name="' . $media['filename'] . '"');
00608                 $this->add_message('Content-Transfer-Encoding: base64');
00609                 $this->add_message('Content-Disposition: attachment;');
00610                 $this->add_message(' filename="' . $media['filename'] . '"');
00611                 $this->add_message('');
00612                 $this->add_message($this->makeBase64($media['content']));
00613             }
00614         }
00615         $this->add_message('--' . $boundary . '--' . LF);
00616     }
00617 
00618 
00619     /**
00620      * this function creates the HTML part of the mail
00621      *
00622      * @param   string      $boundary: the boundary to use
00623      * @return  void
00624      */
00625     public function constructHTML($boundary) {
00626         // If media, then we know, the multipart/related content-type has been set before this function call
00627         if (count($this->theParts['html']['media'])) {
00628             $this->add_message('--' . $boundary);
00629             // HTML has media
00630             $newBoundary = $this->getBoundary();
00631             $this->add_message('Content-Type: multipart/alternative;');
00632             $this->add_message(' boundary="' . $newBoundary . '"');
00633             $this->add_message('Content-Transfer-Encoding: 7bit');
00634             $this->add_message('');
00635 
00636                 // Adding the plaintext/html mix, and use $newBoundary
00637             $this->constructAlternative($newBoundary);
00638             $this->constructHTML_media($boundary);
00639         } else  {
00640             // if no media, just use the $boundary for adding plaintext/html mix
00641             $this->constructAlternative($boundary);
00642         }
00643     }
00644 
00645 
00646     /**
00647      * Here plain is combined with HTML
00648      *
00649      * @param   string      $boundary: the boundary to use
00650      * @return  void
00651      */
00652     public function constructAlternative($boundary) {
00653         $this->add_message('--'.$boundary);
00654 
00655             // plain is added
00656         $this->add_message($this->plain_text_header);
00657         $this->add_message('');
00658         $this->add_message($this->getContent('plain'));
00659         $this->add_message('--' . $boundary);
00660 
00661             // html is added
00662         $this->add_message($this->html_text_header);
00663         $this->add_message('');
00664         $this->add_message($this->getContent('html'));
00665         $this->add_message('--' . $boundary . '--' . LF);
00666     }
00667 
00668 
00669     /**
00670      * Constructs the HTML-part of message if the HTML contains media
00671      *
00672      * @param   string      $boundary: the boundary to use
00673      * @return  void
00674      */
00675     public function constructHTML_media($boundary) {
00676         // media is added
00677         if (is_array($this->theParts['html']['media'])) {
00678             foreach($this->theParts['html']['media'] as $key => $media) {
00679                 if (!$this->mediaList || t3lib_div::inList($this->mediaList, $key)) {
00680                     $this->add_message('--' . $boundary);
00681                     $this->add_message('Content-Type: ' . $media['ctype']);
00682                     $this->add_message('Content-ID: <part' . $key . '.' . $this->messageid . '>');
00683                     $this->add_message('Content-Transfer-Encoding: base64');
00684                     $this->add_message('');
00685                     $this->add_message($this->makeBase64($media['content']));
00686                 }
00687             }
00688         }
00689         $this->add_message('--' . $boundary . '--' . LF);
00690     }
00691 
00692 
00693     /**
00694      * Sends the mail by calling the mail() function in php. On Linux systems this will invoke the MTA
00695      * defined in php.ini (sendmail -t -i by default), on Windows a SMTP must be specified in the sys.ini.
00696      * Most common MTA's on Linux has a Sendmail interface, including Postfix and Exim.
00697      * For setting the return-path correctly, the parameter -f has to be added to the system call to sendmail.
00698      * This obviously does not have any effect on Windows, but on Sendmail compliant systems this works. If safe mode
00699      * is enabled, then extra parameters is not allowed, so a safe mode check is made before the mail() command is
00700      * invoked. When using the -f parameter, some MTA's will put an X-AUTHENTICATION-WARNING saying that
00701      * the return path was modified manually with the -f flag. To disable this warning make sure that the user running
00702      * Apache is in the /etc/mail/trusted-users table.
00703      *
00704      * POSTFIX: With postfix version below 2.0 there is a problem that the -f parameter can not be used in conjunction
00705      * with -t. Postfix will give an error in the maillog:
00706      *
00707      *  cannot handle command-line recipients with -t
00708      *
00709      * The -f parameter is only enabled if the parameter forceReturnPath is enabled in the install tool.
00710      *
00711      * This whole problem of return-path turns out to be quite tricky. If you have a solution that works better, on all
00712      * standard MTA's then we are very open for suggestions.
00713      *
00714      * With time this function should be made such that several ways of sending the mail is possible (local MTA, smtp other).
00715      *
00716      * @return  boolean     Returns whether the mail was sent (successfully accepted for delivery)
00717      */
00718     public function sendTheMail() {
00719         $mailWasSent = false;
00720 
00721             // Sending the mail requires the recipient and message to be set.
00722         if (!trim($this->recipient) || !trim($this->message)) {
00723             return false;
00724         }
00725 
00726             // On windows the -f flag is not used (specific for Sendmail and Postfix),
00727             // but instead the php.ini parameter sendmail_from is used.
00728         $returnPath = ($this->forceReturnPath && strlen($this->returnPath) > 0) ? '-f ' . escapeshellarg($this->returnPath) : '';
00729         if (TYPO3_OS == 'WIN' && $this->returnPath) {
00730             @ini_set('sendmail_from', t3lib_div::normalizeMailAddress($this->returnPath));
00731         }
00732         $recipient = t3lib_div::normalizeMailAddress($this->recipient);
00733 
00734         // If safe mode is on, the fifth parameter to mail is not allowed, so the fix wont work on unix with safe_mode=On
00735         $returnPathPossible = (!ini_get('safe_mode') && $this->forceReturnPath);
00736         if ($returnPathPossible) {
00737             $mailWasSent = t3lib_utility_Mail::mail($recipient,
00738                   $this->subject,
00739                   $this->message,
00740                   $this->headers,
00741                   $returnPath);
00742         } else {
00743             $mailWasSent = t3lib_utility_Mail::mail($recipient,
00744                   $this->subject,
00745                   $this->message,
00746                   $this->headers);
00747         }
00748 
00749             // Auto response
00750         if ($this->auto_respond_msg) {
00751             $theParts = explode('/',$this->auto_respond_msg,2);
00752             $theParts[0] = str_replace('###SUBJECT###', $this->subject, $theParts[0]);
00753             $theParts[1] = str_replace("/",LF,$theParts[1]);
00754             $theParts[1] = str_replace("###MESSAGE###", $this->getContent('plain'), $theParts[1]);
00755             if ($returnPathPossible) {
00756                 $mailWasSent = t3lib_utility_Mail::mail($this->from_email,
00757                     $theParts[0],
00758                     $theParts[1],
00759                     'From: ' . $recipient,
00760                     $returnPath);
00761             } else {
00762                 $mailWasSent = t3lib_utility_Mail::mail($this->from_email,
00763                     $theParts[0],
00764                     $theParts[1],
00765                     'From: ' . $recipient);
00766             }
00767         }
00768         if ($this->returnPath) {
00769             ini_restore('sendmail_from');
00770         }
00771         return $mailWasSent;
00772     }
00773 
00774 
00775     /**
00776      * Returns boundaries
00777      *
00778      * @return  string  the boundary
00779      */
00780     public function getBoundary() {
00781         $this->part++;
00782         return  "----------".uniqid("part_".$this->part."_");
00783     }
00784 
00785 
00786     /**
00787      * Sets the plain-text part. No processing done.
00788      *
00789      * @param   string      $content: the plain content
00790      * @return  void
00791      */
00792     public function setPlain($content) {
00793         $this->theParts['plain']['content'] = $content;
00794     }
00795 
00796 
00797     /**
00798      * Sets the HTML-part. No processing done.
00799      *
00800      * @param   string      $content: the HTML content
00801      * @return  void
00802      */
00803     public function setHtml($content) {
00804         $this->theParts['html']['content'] = $content;
00805     }
00806 
00807 
00808     /**
00809      * Adds a header to the mail. Use this AFTER the setHeaders()-function
00810      *
00811      * @param   string      $header: the header in form of "key: value"
00812      * @return  void
00813      */
00814     public function add_header($header) {
00815             // Mail headers must be ASCII, therefore we convert the whole header to either base64 or quoted_printable
00816         if (!$this->dontEncodeHeader && !stristr($header,'Content-Type') && !stristr($header,'Content-Transfer-Encoding')) {
00817                 // Field tags must not be encoded
00818             $parts = explode(': ',$header,2);
00819             if (count($parts) == 2) {
00820                 $enc = $this->alt_base64 ? 'base64' : 'quoted_printable';
00821                 $parts[1] = t3lib_div::encodeHeader($parts[1], $enc, $this->charset);
00822                 $header = implode(': ', $parts);
00823             }
00824         }
00825 
00826         $this->headers .= $header.LF;
00827     }
00828 
00829 
00830     /**
00831      * Adds a line of text to the mail-body. Is normally used internally
00832      *
00833      * @param   string      $msg: the message to add
00834      * @return  void
00835      */
00836     public function add_message($msg) {
00837         $this->message .= $msg.LF;
00838     }
00839 
00840 
00841     /**
00842      * returns the content specified by the type (plain, html etc.)
00843      *
00844      * @param   string      $type: the content type, can either plain or html
00845      * @return  void
00846      */
00847     public function getContent($type) {
00848         return $this->theParts[$type]['content'];
00849     }
00850 
00851 
00852     /**
00853      * shows a preview of the email of the headers and the message
00854      *
00855      * @return  void
00856      */
00857     public function preview() {
00858         echo nl2br(htmlspecialchars($this->headers));
00859         echo "<BR>";
00860         echo nl2br(htmlspecialchars($this->message));
00861     }
00862 
00863 
00864 
00865 
00866 
00867 
00868 
00869 
00870     /****************************************************
00871      *
00872      * Functions for acquiring attachments, HTML, analyzing and so on  **
00873      *
00874      ***************************************************/
00875 
00876     /**
00877      * Fetches the HTML-content from either url og local serverfile
00878      *
00879      * @param   string      $file: the file to load
00880      * @return  boolean     whether the data was fetched or not
00881      */
00882     public function fetchHTML($file) {
00883             // Fetches the content of the page
00884         $this->theParts['html']['content'] = $this->getURL($file);
00885         if ($this->theParts['html']['content']) {
00886             $addr = $this->extParseUrl($file);
00887             $path = ($addr['scheme']) ? $addr['scheme'].'://'.$addr['host'].(($addr['port'])?':'.$addr['port']:'').(($addr['filepath'])?$addr['filepath']:'/') : $addr['filepath'];
00888             $this->theParts['html']['path'] = $path;
00889             return true;
00890         } else  {
00891             return false;
00892         }
00893     }
00894 
00895 
00896     /**
00897      * Fetches the mediafiles which are found by extractMediaLinks()
00898      *
00899      * @return  void
00900      */
00901     public function fetchHTMLMedia() {
00902         if (!is_array($this->theParts['html']['media']) || !count($this->theParts['html']['media'])) return;
00903         foreach ($this->theParts['html']['media'] as $key => $media) {
00904                 // fetching the content and the mime-type
00905             $picdata = $this->getExtendedURL($this->theParts['html']['media'][$key]['absRef']);
00906             if (is_array($picdata)) {
00907                 $this->theParts['html']['media'][$key]['content'] = $picdata['content'];
00908                 $this->theParts['html']['media'][$key]['ctype']   = $picdata['content_type'];
00909             }
00910         }
00911     }
00912 
00913 
00914     /**
00915      * extracts all media-links from $this->theParts['html']['content']
00916      *
00917      * @return  void
00918      */
00919     public function extractMediaLinks() {
00920         $html_code = $this->theParts['html']['content'];
00921         $attribRegex = $this->tag_regex(array('img','table','td','tr','body','iframe','script','input','embed'));
00922 
00923             // split the document by the beginning of the above tags
00924         $codepieces = preg_split($attribRegex, $html_code);
00925         $len = strlen($codepieces[0]);
00926         $pieces = count($codepieces);
00927         $reg = array();
00928         for ($i = 1; $i < $pieces; $i++) {
00929             $tag = strtolower(strtok(substr($html_code,$len+1,10),' '));
00930             $len += strlen($tag)+strlen($codepieces[$i])+2;
00931             $dummy = preg_match('/[^>]*/', $codepieces[$i], $reg);
00932             $attributes = $this->get_tag_attributes($reg[0]);   // Fetches the attributes for the tag
00933             $imageData = array();
00934 
00935                 // Finds the src or background attribute
00936             $imageData['ref'] = ($attributes['src'] ? $attributes['src'] : $attributes['background']);
00937             if ($imageData['ref']) {
00938                     // find out if the value had quotes around it
00939                 $imageData['quotes'] = (substr($codepieces[$i], strpos($codepieces[$i], $imageData['ref'])-1,1) == '"') ? '"' : '';
00940                     // subst_str is the string to look for, when substituting lateron
00941                 $imageData['subst_str'] = $imageData['quotes'].$imageData['ref'].$imageData['quotes'];
00942                 if ($imageData['ref'] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
00943                     $this->image_fullpath_list .= "|".$imageData['subst_str']."|";
00944                     $imageData['absRef'] = $this->absRef($imageData['ref']);
00945                     $imageData['tag'] = $tag;
00946                     $imageData['use_jumpurl'] = $attributes['dmailerping']?1:0;
00947                     $this->theParts['html']['media'][] = $imageData;
00948                 }
00949             }
00950         }
00951 
00952             // Extracting stylesheets
00953         $attribRegex = $this->tag_regex(array('link'));
00954             // Split the document by the beginning of the above tags
00955         $codepieces = preg_split($attribRegex, $html_code);
00956         $pieces = count($codepieces);
00957         for ($i = 1; $i < $pieces; $i++) {
00958             $dummy = preg_match('/[^>]*/', $codepieces[$i], $reg);
00959                 // fetches the attributes for the tag
00960             $attributes = $this->get_tag_attributes($reg[0]);
00961             $imageData = array();
00962             if (strtolower($attributes['rel']) == 'stylesheet' && $attributes['href']) {
00963                     // Finds the src or background attribute
00964                 $imageData['ref'] = $attributes['href'];
00965                     // Finds out if the value had quotes around it
00966                 $imageData['quotes'] = (substr($codepieces[$i],strpos($codepieces[$i], $imageData['ref'])-1,1) == '"') ? '"' : '';
00967                     // subst_str is the string to look for, when substituting lateron
00968                 $imageData['subst_str'] = $imageData['quotes'].$imageData['ref'].$imageData['quotes'];
00969                 if ($imageData['ref'] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
00970                     $this->image_fullpath_list .= "|".$imageData["subst_str"]."|";
00971                     $imageData['absRef'] = $this->absRef($imageData["ref"]);
00972                     $this->theParts['html']['media'][] = $imageData;
00973                 }
00974             }
00975         }
00976 
00977             // fixes javascript rollovers
00978         $codepieces = explode('.src', $html_code);
00979         $pieces = count($codepieces);
00980         $expr = '/^[^'.quotemeta('"').quotemeta("'").']*/';
00981         for($i = 1; $i < $pieces; $i++) {
00982             $temp = $codepieces[$i];
00983             $temp = trim(str_replace('=','',trim($temp)));
00984             preg_match($expr,substr($temp,1,strlen($temp)),$reg);
00985             $imageData['ref'] = $reg[0];
00986             $imageData['quotes'] = substr($temp,0,1);
00987                 // subst_str is the string to look for, when substituting lateron
00988             $imageData['subst_str'] = $imageData['quotes'].$imageData['ref'].$imageData['quotes'];
00989             $theInfo = $this->split_fileref($imageData['ref']);
00990 
00991             switch ($theInfo['fileext']) {
00992                 case 'gif':
00993                 case 'jpeg':
00994                 case 'jpg':
00995                     if ($imageData['ref'] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
00996                         $this->image_fullpath_list .= "|".$imageData['subst_str']."|";
00997                         $imageData['absRef'] = $this->absRef($imageData['ref']);
00998                         $this->theParts['html']['media'][] = $imageData;
00999                     }
01000                 break;
01001             }
01002         }
01003     }
01004 
01005 
01006     /**
01007      * extracts all hyper-links from $this->theParts["html"]["content"]
01008      *
01009      * @return  void
01010      */
01011     public function extractHyperLinks() {
01012         $html_code = $this->theParts['html']['content'];
01013         $attribRegex = $this->tag_regex(array('a','form','area'));
01014         $codepieces = preg_split($attribRegex, $html_code); // Splits the document by the beginning of the above tags
01015         $len = strlen($codepieces[0]);
01016         $pieces = count($codepieces);
01017         for($i = 1; $i < $pieces; $i++) {
01018             $tag = strtolower(strtok(substr($html_code,$len+1,10)," "));
01019             $len += strlen($tag) + strlen($codepieces[$i]) + 2;
01020 
01021             $dummy = preg_match('/[^>]*/', $codepieces[$i], $reg);
01022                 // Fetches the attributes for the tag
01023             $attributes = $this->get_tag_attributes($reg[0]);
01024             $hrefData = array();
01025             $hrefData['ref'] = ($attributes['href'] ? $attributes['href'] : $hrefData['ref'] = $attributes['action']);
01026             if ($hrefData['ref']) {
01027                     // Finds out if the value had quotes around it
01028                 $hrefData['quotes'] = (substr($codepieces[$i],strpos($codepieces[$i], $hrefData["ref"])-1,1) == '"') ? '"' : '';
01029                     // subst_str is the string to look for, when substituting lateron
01030                 $hrefData['subst_str'] = $hrefData['quotes'].$hrefData['ref'].$hrefData['quotes'];
01031                 if ($hrefData['ref'] && substr(trim($hrefData['ref']),0,1) != "#" && !strstr($this->href_fullpath_list,"|".$hrefData['subst_str']."|")) {
01032                     $this->href_fullpath_list .= "|".$hrefData['subst_str']."|";
01033                     $hrefData['absRef'] = $this->absRef($hrefData['ref']);
01034                     $hrefData['tag'] = $tag;
01035                     $this->theParts['html']['hrefs'][] = $hrefData;
01036                 }
01037             }
01038         }
01039             // Extracts TYPO3 specific links made by the openPic() JS function
01040         $codepieces = explode("onClick=\"openPic('", $html_code);
01041         $pieces = count($codepieces);
01042         for($i = 1; $i < $pieces; $i++) {
01043             $showpic_linkArr = explode("'",$codepieces[$i]);
01044             $hrefData['ref'] = $showpic_linkArr[0];
01045             if ($hrefData['ref']) {
01046                 $hrefData['quotes'] = "'";
01047                     // subst_str is the string to look for, when substituting lateron
01048                 $hrefData['subst_str'] = $hrefData['quotes'].$hrefData['ref'].$hrefData['quotes'];
01049                 if ($hrefData['ref'] && !strstr($this->href_fullpath_list,"|".$hrefData['subst_str']."|")) {
01050                     $this->href_fullpath_list .= "|".$hrefData['subst_str']."|";
01051                     $hrefData['absRef'] = $this->absRef($hrefData['ref']);
01052                     $this->theParts['html']['hrefs'][] = $hrefData;
01053                 }
01054             }
01055         }
01056     }
01057 
01058 
01059     /**
01060      * extracts all media-links from $this->theParts["html"]["content"]
01061      *
01062      * @return  array   two-dimensional array with information about each frame
01063      */
01064     public function extractFramesInfo() {
01065         $htmlCode = $this->theParts['html']['content'];
01066         $info = array();
01067         if (strpos(' '.$htmlCode,'<frame ')) {
01068             $attribRegex = $this->tag_regex('frame');
01069                 // Splits the document by the beginning of the above tags
01070             $codepieces = preg_split($attribRegex, $htmlCode, 1000000);
01071             $pieces = count($codepieces);
01072             for($i = 1; $i < $pieces; $i++) {
01073                 $dummy = preg_match('/[^>]*/', $codepieces[$i], $reg);
01074                     // Fetches the attributes for the tag
01075                 $attributes = $this->get_tag_attributes($reg[0]);
01076                 $frame = array();
01077                 $frame['src'] = $attributes['src'];
01078                 $frame['name'] = $attributes['name'];
01079                 $frame['absRef'] = $this->absRef($frame['src']);
01080                 $info[] = $frame;
01081             }
01082             return $info;
01083         }
01084     }
01085 
01086 
01087     /**
01088      * This function substitutes the media-references in $this->theParts["html"]["content"]
01089      *
01090      * @param   boolean     $absolute: If true, then the refs are substituted with http:// ref's indstead of Content-ID's (cid).
01091      * @return  void
01092      */
01093     public function substMediaNamesInHTML($absolute) {
01094         if (is_array($this->theParts['html']['media'])) {
01095             foreach ($this->theParts['html']['media'] as $key => $val) {
01096                 if ($val['use_jumpurl'] && $this->jumperURL_prefix) {
01097                     $subst = $this->jumperURL_prefix.t3lib_div::rawUrlEncodeFP($val['absRef']);
01098                 } else {
01099                     $subst = ($absolute) ? $val['absRef'] : 'cid:part'.$key.'.'.$this->messageid;
01100                 }
01101                 $this->theParts['html']['content'] = str_replace(
01102                     $val['subst_str'],
01103                     $val['quotes'] . $subst . $val['quotes'],
01104                     $this->theParts['html']['content']);
01105             }
01106         }
01107         if (!$absolute) {
01108             $this->fixRollOvers();
01109         }
01110     }
01111 
01112 
01113     /**
01114      * This function substitutes the hrefs in $this->theParts["html"]["content"]
01115      *
01116      * @return  void
01117      */
01118     public function substHREFsInHTML() {
01119         if (!is_array($this->theParts['html']['hrefs'])) return;
01120         foreach ($this->theParts['html']['hrefs'] as $key => $val) {
01121                 // Form elements cannot use jumpurl!
01122             if ($this->jumperURL_prefix && $val['tag'] != 'form') {
01123                 if ($this->jumperURL_useId) {
01124                     $substVal = $this->jumperURL_prefix.$key;
01125                 } else {
01126                     $substVal = $this->jumperURL_prefix.t3lib_div::rawUrlEncodeFP($val['absRef']);
01127                 }
01128             } else {
01129                 $substVal = $val['absRef'];
01130             }
01131             $this->theParts['html']['content'] = str_replace(
01132                 $val['subst_str'],
01133                 $val['quotes'] . $substVal . $val['quotes'],
01134                 $this->theParts['html']['content']);
01135         }
01136     }
01137 
01138 
01139     /**
01140      *  This substitutes the http:// urls in plain text with links
01141      *
01142      * @param   string      $content: the content to use to substitute
01143      * @return  string      the changed content
01144      */
01145     public function substHTTPurlsInPlainText($content) {
01146         if (!$this->jumperURL_prefix) return $content;
01147 
01148         $textpieces = explode("http://", $content);
01149         $pieces = count($textpieces);
01150         $textstr = $textpieces[0];
01151         for($i = 1; $i<$pieces; $i++) {
01152             $len = strcspn($textpieces[$i],chr(32).TAB.CRLF);
01153             if (trim(substr($textstr,-1)) == '' && $len) {
01154                 $lastChar = substr($textpieces[$i],$len-1,1);
01155                 if (!preg_match('/[A-Za-z0-9\/#]/',$lastChar)) {
01156                     $len--;
01157                 }
01158 
01159                 $parts = array();
01160                 $parts[0] = "http://".substr($textpieces[$i],0,$len);
01161                 $parts[1] = substr($textpieces[$i],$len);
01162 
01163                 if ($this->jumperURL_useId) {
01164                     $this->theParts['plain']['link_ids'][$i] = $parts[0];
01165                     $parts[0] = $this->jumperURL_prefix.'-'.$i;
01166                 } else {
01167                     $parts[0] = $this->jumperURL_prefix.t3lib_div::rawUrlEncodeFP($parts[0]);
01168                 }
01169                 $textstr .= $parts[0].$parts[1];
01170             } else {
01171                 $textstr .= 'http://'.$textpieces[$i];
01172             }
01173         }
01174         return $textstr;
01175     }
01176 
01177 
01178     /**
01179      * JavaScript rollOvers cannot support graphics inside of mail.
01180      * If these exists we must let them refer to the absolute url. By the way:
01181      * Roll-overs seems to work only on some mail-readers and so far I've seen it
01182      * work on Netscape 4 message-center (but not 4.5!!)
01183      *
01184      * @return  void
01185      */
01186     public function fixRollOvers() {
01187         $newContent = '';
01188         $items = explode('.src',$this->theParts['html']['content']);
01189         if (count($items) <= 1) return;
01190 
01191         foreach($items as $key => $part) {
01192             $sub = substr($part, 0, 200);
01193             if (preg_match('/cid:part[^ "\']*/',$sub,$reg)) {
01194                     // The position of the string
01195                 $thePos = strpos($part,$reg[0]);
01196                     // Finds the id of the media...
01197                 preg_match('/cid:part([^\.]*).*/',$sub,$reg2);
01198                 $theSubStr = $this->theParts['html']['media'][intval($reg2[1])]['absRef'];
01199                 if ($thePos && $theSubStr) {
01200                     // ... and substitutes the javaScript rollover image with this instead
01201                     // If the path is NOT and url, the reference is set to nothing
01202                     if (!strpos(' '.$theSubStr, 'http://')) {
01203                         $theSubStr = 'http://';
01204                     }
01205                     $part = substr($part, 0, $thePos) . $theSubStr . substr($part,$thePos+strlen($reg[0]),strlen($part));
01206                 }
01207             }
01208             $newContent .= $part . ((($key+1) != count($items)) ? '.src' : '');
01209         }
01210         $this->theParts['html']['content'] = $newContent;
01211     }
01212 
01213 
01214 
01215 
01216 
01217 
01218 
01219 
01220     /*******************************************
01221      *
01222      * File and URL-functions
01223      *
01224      *******************************************/
01225 
01226     /**
01227      * Returns base64-encoded content, which is broken every 76 character
01228      *
01229      * @param   string      $inputstr: the string to encode
01230      * @return  string      the encoded string
01231      */
01232     public function makeBase64($inputstr) {
01233         return chunk_split(base64_encode($inputstr));
01234     }
01235 
01236 
01237     /**
01238      * reads the URL or file and determines the Content-type by either guessing or opening a connection to the host
01239      *
01240      * @param   string      $url: the URL to get information of
01241      * @return  mixed       either false or the array with information
01242      */
01243     public function getExtendedURL($url) {
01244         $res = array();
01245         $res['content'] = $this->getURL($url);
01246         if (!$res['content']) return false;
01247         $pathInfo = parse_url($url);
01248         $fileInfo = $this->split_fileref($pathInfo['path']);
01249         switch ($fileInfo['fileext']) {
01250             case 'gif':
01251             case 'png':
01252                 $res['content_type'] = 'image/'.$fileInfo['fileext'];
01253                 break;
01254             case 'jpg':
01255             case 'jpeg':
01256                 $res['content_type'] = 'image/jpeg';
01257                 break;
01258             case 'html':
01259             case 'htm':
01260                 $res['content_type'] = 'text/html';
01261                 break;
01262             case 'css':
01263                 $res['content_type'] = 'text/css';
01264                 break;
01265             case 'swf':
01266                 $res['content_type'] = 'application/x-shockwave-flash';
01267                 break;
01268             default:
01269                 $res['content_type'] = $this->getMimeType($url);
01270         }
01271         return $res;
01272     }
01273 
01274 
01275     /**
01276      * Adds HTTP user and password (from $this->http_username) to a URL
01277      *
01278      * @param   string      $url: the URL
01279      * @return  string      the URL with the added values
01280      */
01281     public function addUserPass($url) {
01282         $user = $this->http_username;
01283         $pass = $this->http_password;
01284         $matches = array();
01285         if ($user && $pass && preg_match('/^(https?:\/\/)/', $url, $matches)) {
01286             return $matches[1].$user.':'.$pass.'@'.substr($url,strlen($matches[1]));
01287         }
01288         return $url;
01289     }
01290 
01291 
01292     /**
01293      * reads a url or file
01294      *
01295      * @param   string      $url: the URL to fetch
01296      * @return  string      the content of the URL
01297      */
01298     public function getURL($url) {
01299         $url = $this->addUserPass($url);
01300         return t3lib_div::getURL($url);
01301     }
01302 
01303 
01304     /**
01305      * reads a url or file and strips the HTML-tags AND removes all
01306      * empty lines. This is used to read plain-text out of a HTML-page
01307      *
01308      * @param   string      $url: the URL to load
01309      * @return  the content
01310      */
01311     public function getStrippedURL($url) {
01312         $content = '';
01313         if ($fd = fopen($url, "rb")) {
01314             while (!feof($fd)) {
01315                 $line = fgetss($fd, 5000);
01316                 if (trim($line)) {
01317                     $content .= trim($line) . LF;
01318                 }
01319             }
01320             fclose($fd);
01321         }
01322         return $content;
01323     }
01324 
01325 
01326     /**
01327      * This function returns the mime type of the file specified by the url
01328      *
01329      * @param   string      $url: the url
01330      * @return  string      $mimeType: the mime type found in the header
01331      */
01332     public function getMimeType($url) {
01333         $mimeType = '';
01334         $headers = trim(t3lib_div::getURL($url, 2));
01335         if ($headers) {
01336             $matches = array();
01337             if (preg_match('/(Content-Type:[\s]*)([a-zA-Z_0-9\/\-\.\+]*)([\s]|$)/', $headers, $matches)) {
01338                 $mimeType = trim($matches[2]);
01339             }
01340         }
01341         return $mimeType;
01342     }
01343 
01344 
01345     /**
01346      * Returns the absolute address of a link. This is based on
01347      * $this->theParts["html"]["path"] being the root-address
01348      *
01349      * @param   string      $ref: address to use
01350      * @return  string      the absolute address
01351      */
01352     public function absRef($ref) {
01353         $ref = trim($ref);
01354         $info = parse_url($ref);
01355         if ($info['scheme']) {
01356             return $ref;
01357         } elseif (preg_match('/^\//',$ref)) {
01358             $addr = parse_url($this->theParts['html']['path']);
01359             return  $addr['scheme'].'://'.$addr['host'].($addr['port']?':'.$addr['port']:'').$ref;
01360         } else {
01361             // If the reference is relative, the path is added, in order for us to fetch the content
01362             return $this->theParts['html']['path'] . $ref;
01363         }
01364     }
01365 
01366 
01367     /**
01368      * Returns information about a file reference
01369      *
01370      * @param   string      $fileref: the file to use
01371      * @return  array       path, filename, filebody, fileext
01372      */
01373     public function split_fileref($fileref) {
01374         $info = array();
01375         if (preg_match('/(.*\/)(.*)$/', $fileref, $reg)) {
01376             $info['path'] = $reg[1];
01377             $info['file'] = $reg[2];
01378         } else  {
01379             $info['path'] = '';
01380             $info['file'] = $fileref;
01381         }
01382         $reg = '';
01383         if (preg_match('/(.*)\.([^\.]*$)/', $info['file'], $reg)) {
01384             $info['filebody'] = $reg[1];
01385             $info['fileext'] = strtolower($reg[2]);
01386             $info['realFileext'] = $reg[2];
01387         } else  {
01388             $info['filebody'] = $info['file'];
01389             $info['fileext'] = '';
01390         }
01391         return $info;
01392     }
01393 
01394 
01395     /**
01396      * Returns an array with file or url-information
01397      *
01398      * @param   string      $path: url to check
01399      * @return  array       information about the path / URL
01400      */
01401     public function extParseUrl($path) {
01402         $res = parse_url($path);
01403         preg_match('/(.*\/)([^\/]*)$/', $res['path'], $reg);
01404         $res['filepath'] = $reg[1];
01405         $res['filename'] = $reg[2];
01406         return $res;
01407     }
01408 
01409 
01410     /**
01411      * Creates a regular expression out of a list of tags
01412      *
01413      * @param   mixed       $tagArray: the list of tags (either as array or string if it is one tag)
01414      * @return  string      the regular expression
01415      */
01416     public function tag_regex($tags) {
01417         $tags = (!is_array($tags) ? array($tags) : $tags);
01418         $regexp = '/';
01419         $c = count($tags);
01420         foreach($tags as $tag) {
01421             $c--;
01422             $regexp .= '<' . sql_regcase($tag) . "[[:space:]]" . (($c) ? '|' : '');
01423         }
01424         return $regexp . '/';
01425     }
01426 
01427 
01428     /**
01429      * This function analyzes a HTML tag
01430      * If an attribute is empty (like OPTION) the value of that key is just empty. Check it with is_set();
01431      *
01432      * @param   string      $tag: is either like this "<TAG OPTION ATTRIB=VALUE>" or
01433      *              this " OPTION ATTRIB=VALUE>" which means you can omit the tag-name
01434      * @return  array       array with attributes as keys in lower-case
01435      */
01436     public function get_tag_attributes($tag) {
01437         $attributes = array();
01438         $tag = ltrim(preg_replace('/^<[^ ]*/','',trim($tag)));
01439         $tagLen = strlen($tag);
01440         $safetyCounter = 100;
01441             // Find attribute
01442         while ($tag) {
01443             $value = '';
01444             $reg = preg_split('/[[:space:]=>]/', $tag, 2);
01445             $attrib = $reg[0];
01446 
01447             $tag = ltrim(substr($tag,strlen($attrib),$tagLen));
01448             if (substr($tag,0,1) == '=') {
01449                 $tag = ltrim(substr($tag,1,$tagLen));
01450                 if (substr($tag,0,1) == '"') {
01451                         // Quotes around the value
01452                     $reg = explode('"',substr($tag,1,$tagLen),2);
01453                     $tag = ltrim($reg[1]);
01454                     $value = $reg[0];
01455                 } else {
01456                         // No quotes around value
01457                     preg_match('/^([^[:space:]>]*)(.*)/',$tag,$reg);
01458                     $value = trim($reg[1]);
01459                     $tag = ltrim($reg[2]);
01460                     if (substr($tag,0,1) == '>') {
01461                         $tag = '';
01462                     }
01463                 }
01464             }
01465             $attributes[strtolower($attrib)] = $value;
01466             $safetyCounter--;
01467             if ($safetyCounter < 0) break;
01468         }
01469         return $attributes;
01470     }
01471 
01472 
01473     /**
01474      * Implementation of quoted-printable encode.
01475      * This function was a duplicate of t3lib_div::quoted_printable, thus it's going to be removed.
01476      * Deprecated since TYPO3 4.0
01477      *
01478      * @param   string      Content to encode
01479      * @return  string      The QP encoded string
01480      * @deprecated since TYPO3 4.0, remove in TYPO 4.3
01481      */
01482     public function quoted_printable($string) {
01483         return t3lib_div::quoted_printable($string, 76);
01484     }
01485 
01486 
01487     /**
01488      * Converts a name field
01489      * Deprecated since TYPO3 4.0
01490      *
01491      * @param   string      $name: the name
01492      * @return  string      the name
01493      * @deprecated since TYPO3 4.0, remove in TYPO3 4.3
01494      */
01495     public function convertName($name) {
01496         return $name;
01497     }
01498 }
01499 
01500 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_htmlmail.php']) {
01501     include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_htmlmail.php']);
01502 }
01503 
01504 ?>

Generated on Sat Jul 24 04:17:17 2010 for TYPO3 API by  doxygen 1.4.7