2005-02-04 Michael Stucki <michael@typo3.org>
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_htmlmail.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2004 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * HTML mail class
29 *
30 * $Id$
31 *
32 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
33 */
34 /**
35 * [CLASS/FUNCTION INDEX of SCRIPT]
36 *
37 *
38 *
39 * 190: class t3lib_htmlmail
40 * 253: function start ()
41 * 263: function useBase64()
42 * 275: function encodeMsg($content)
43 * 285: function addPlain ($content)
44 * 297: function addAttachment($file)
45 * 315: function addHTML ($file)
46 * 338: function extractHtmlInit($html,$url)
47 * 349: function send($recipient)
48 *
49 * SECTION: Main functions
50 * 378: function setHeaders ()
51 * 432: function setRecipient ($recip)
52 * 450: function getHTMLContentType()
53 * 459: function setContent()
54 * 486: function constructMixed ($boundary)
55 * 525: function constructHTML ($boundary)
56 * 548: function constructAlternative($boundary)
57 * 569: function constructHTML_media ($boundary)
58 * 602: function sendTheMail ()
59 * 664: function getBoundary()
60 * 676: function setPlain ($content)
61 * 687: function setHtml ($content)
62 * 698: function add_header ($header)
63 * 709: function add_message ($string)
64 * 720: function getContent($type)
65 * 729: function preview()
66 *
67 * SECTION: Functions for acquiring attachments, HTML, analyzing and so on **
68 * 757: function fetchHTML($file)
69 * 775: function fetchHTMLMedia()
70 * 796: function extractMediaLinks()
71 * 872: function extractHyperLinks()
72 * 921: function extractFramesInfo()
73 * 947: function substMediaNamesInHTML($absolute)
74 * 974: function substHREFsInHTML()
75 * 1002: function substHTTPurlsInPlainText($content)
76 * 1042: function fixRollOvers()
77 *
78 * SECTION: File and URL-functions
79 * 1089: function makeBase64($inputstr)
80 * 1100: function getExtendedURL($url)
81 * 1120: function addUserPass($url)
82 * 1135: function getURL($url)
83 * 1156: function getStrippedURL($url)
84 * 1177: function getMimeType($url)
85 * 1206: function absRef($ref)
86 * 1226: function split_fileref($fileref)
87 * 1253: function extParseUrl($path)
88 * 1268: function tag_regex($tagArray)
89 * 1290: function get_tag_attributes($tag)
90 * 1330: function quoted_printable($string)
91 * 1368: function convertName($name)
92 *
93 * TOTAL FUNCTIONS: 46
94 * (This index is automatically created/updated by the extension "extdeveval")
95 *
96 */
97
98
99
100
101
102
103
104
105
106 /**
107 * NOTES on MIME mail structures:
108 *
109 * Plain + HTML
110 * multipart/alternative (text, html)
111 * multipart/alternative (text, html)
112 *
113 * Plain + HTML + image
114 * multipart/related (m/a, cids)
115 * multipart/alternative (text, html)
116 *
117 * multipart/related (m/a, cids)
118 * multipart/alternative (text, html)
119 *
120 * plain + attachment
121 * multipart/mixed
122 *
123 * HTML + Attachment:
124 * multipart/mixed (text/html , attachments)
125 *
126 * Plain + HTML + Attachments:
127 * multipart/mixed (m/a, attachments)
128 * multipart/alternative (text, html)
129 *
130 * Plain + HTML + image + attachment
131 *
132 * Outlook expr.
133 * multipart/mixed (m/r, attachments)
134 * multipart/related (m/a, cids)
135 * multipart/alternative (text, html)
136 *
137 *
138 *
139 * FROM RFC 1521:
140 *
141 * 5.1 Quoted-Printable Content-Transfer-Encoding
142 * 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.
143 *
144 * In this encoding, octets are to be represented as determined by the following rules:
145 * 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.
146 * 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).
147 * 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
148 * "=" 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.
149 * 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.
150 * 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.
151 * 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:
152 * Now's the time for all folk to come to the aid of their country.
153 *
154 * This can be represented, in the Quoted-Printable encoding, as
155 *
156 * Now's the time =
157 * for all folk to come=
158 * to the aid of their country.
159 *
160 * 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.
161 * 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.)
162 * 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
163 * !"#$@[\]^`{|}~
164 * according to rule #1. See Appendix B for more information.
165 * 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.
166 * 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
167 * platforms with different line break conventions.
168 * For formalists, the syntax of quoted-printable data is described by the following grammar:
169 *
170 * quoted-printable := ([*(ptext / SPACE / TAB) ptext] ["="] CRLF)
171 * ; Maximum line length of 76 characters excluding CRLF
172 *
173 * ptext := octet /<any ASCII character except "=", SPACE, or TAB>
174 * ; characters not listed as "mail-safe" in Appendix B
175 * ; are also not recommended.
176 *
177 * octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F")
178 * ; octet must be used for characters > 127, =, SPACE, or TAB,
179 * ; and is recommended for any characters not listed in
180 * ; Appendix B as "mail-safe".
181 */
182
183 /**
184 * HTML mail class
185 *
186 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
187 * @package TYPO3
188 * @subpackage t3lib
189 */
190 class t3lib_htmlmail {
191 // Headerinfo:
192 var $recipient = "recipient@whatever.com";
193 var $recipient_copy = ""; // This recipient (or list of...) will also receive the mail. Regard it as a copy.
194 var $subject = "This is the subject";
195 var $from_email = "sender@php-mailer.com";
196 var $from_name = "Mr. Sender";
197 var $replyto_email = "reply@mailer.com";
198 var $replyto_name = "Mr. Reply";
199 var $organisation = "Your Company";
200 var $priority = 3; // 1 = highest, 5 = lowest, 3 = normal
201 var $mailer = "PHP mailer"; // X-mailer
202 var $alt_base64=0;
203 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.
204 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
205 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
206 var $http_password="";
207 var $http_username="";
208 var $postfix_version1=false;
209
210 // Internal
211
212 /* This is how the $theParts-array is normally looking
213 var $theParts = Array(
214 "plain" => Array (
215 "content"=> ""
216 ),
217 "html" => Array (
218 "content"=> "",
219 "path" => "",
220 "media" => Array(),
221 "hrefs" => Array()
222 ),
223 "attach" => Array ()
224 );
225 */
226 var $theParts = Array();
227
228 var $messageid = "";
229 var $returnPath = "";
230 var $Xid = "";
231
232 var $headers = "";
233 var $message = "";
234 var $part=0;
235 var $image_fullpath_list = "";
236 var $href_fullpath_list = "";
237
238 var $plain_text_header = "Content-Type: text/plain; charset=iso-8859-1\nContent-Transfer-Encoding: quoted-printable";
239 var $html_text_header = "Content-Type: text/html; charset=iso-8859-1\nContent-Transfer-Encoding: quoted-printable";
240
241
242
243
244
245
246
247
248 /**
249 * Constructor for the class. Make a check to see if Postfix version below 2.0 is used.
250 * If this is the case all calls to mail() must not be called with the -f parameter to correctly set
251 * the Return-Path header.
252 * @return [type] ...
253 */
254 function t3lib_htmlmail () {
255 if(!ini_get('safe_mode')) {
256 $res = Array();
257 if(file_exists('/usr/sbin/postconf')) {
258 @exec('/usr/sbin/postconf mail_version',$res);
259 if(!empty($res[0])) {
260 $temp = explode("=",$res[0]);
261 list($major,$minor,$micro) = explode(".",trim($temp[1]));
262 if($major == 1) {
263 $this->postfix_version1 = true;
264 }
265 }
266 }
267 }
268 }
269
270 /**
271 * @return [type] ...
272 */
273 function start () {
274 // Sets the message id
275 $this->messageid = md5(microtime()).'@domain.tld';
276
277 }
278
279 /**
280 * [Describe function...]
281 *
282 * @return [type] ...
283 */
284 function useBase64() {
285 $this->plain_text_header = 'Content-Type: text/plain; charset=iso-8859-1'.chr(10).'Content-Transfer-Encoding: base64';
286 $this->html_text_header = 'Content-Type: text/html; charset=iso-8859-1'.chr(10).'Content-Transfer-Encoding: base64';
287 $this->alt_base64=1;
288 }
289
290 /**
291 * [Describe function...]
292 *
293 * @param [type] $content: ...
294 * @return [type] ...
295 */
296 function encodeMsg($content) {
297 return $this->alt_base64 ? $this->makeBase64($content) : $this->quoted_printable($content);
298 }
299
300 /**
301 * [Describe function...]
302 *
303 * @param [type] $content: ...
304 * @return [type] ...
305 */
306 function addPlain ($content) {
307 // Adds plain-text and qp-encodes it
308 $content=$this->substHTTPurlsInPlainText($content);
309 $this->setPlain($this->encodeMsg($content));
310 }
311
312 /**
313 * [Describe function...]
314 *
315 * @param [type] $file: ...
316 * @return [type] ...
317 */
318 function addAttachment($file) {
319 // Adds an attachment to the mail
320 $theArr = $this->getExtendedURL($file); // We fetch the content and the mime-type
321 if ($theArr) {
322 if (!$theArr["content_type"]){$theArr["content_type"]="application/octet-stream";}
323 $temp = $this->split_fileref($file);
324 $theArr["filename"]= (($temp["file"])?$temp["file"]:(strpos(" ".$theArr["content_type"],"htm")?"index.html":"unknown"));
325 $this->theParts["attach"][]=$theArr;
326 return true;
327 } else { return false;}
328 }
329
330 /**
331 * [Describe function...]
332 *
333 * @param [type] $file: ...
334 * @return [type] ...
335 */
336 function addHTML ($file) {
337 // Adds HTML and media, encodes it from a URL or file
338 $status = $this->fetchHTML($file);
339 // debug(md5($status));
340 if (!$status) {return false;}
341 if ($this->extractFramesInfo()) {
342 return "Document was a frameset. Stopped";
343 }
344 $this->extractMediaLinks();
345 $this->extractHyperLinks();
346 $this->fetchHTMLMedia();
347 $this->substMediaNamesInHTML(0); // 0 = relative
348 $this->substHREFsInHTML();
349 $this->setHTML($this->encodeMsg($this->theParts["html"]["content"]));
350 }
351
352 /**
353 * External used to extract HTML-parts
354 *
355 * @param [type] $html: ...
356 * @param [type] $url: ...
357 * @return [type] ...
358 */
359 function extractHtmlInit($html,$url) {
360 $this->theParts["html"]["content"]=$html;
361 $this->theParts["html"]["path"]=$url;
362 }
363
364 /**
365 * [Describe function...]
366 *
367 * @param [type] $recipient: ...
368 * @return [type] ...
369 */
370 function send($recipient) {
371 // This function sends the mail to one $recipient
372 if ($recipient) {$this->recipient = $recipient;}
373 $this->setHeaders();
374 $this->setContent();
375 $this->sendTheMail();
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389
390 /*****************************************
391 *
392 * Main functions
393 *
394 *****************************************/
395
396 /**
397 * @return [type] ...
398 */
399 function setHeaders () {
400 // Clears the header-string and sets the headers based on object-vars.
401 $this->headers = "";
402 // Message_id
403 $this->add_header("Message-ID: <".$this->messageid.">");
404 // Return path
405 if ($this->returnPath) {
406 $this->add_header("Return-Path: ".$this->returnPath);
407 }
408 // X-id
409 if ($this->Xid) {
410 $this->add_header("X-Typo3MID: ".$this->Xid);
411 }
412
413 // From
414 if ($this->from_email) {
415 if ($this->from_name) {
416 $name = $this->convertName($this->from_name);
417 $this->add_header("From: $name <$this->from_email>");
418 } else {
419 $this->add_header("From: $this->from_email");
420 }
421 }
422 // Reply
423 if ($this->replyto_email) {
424 if ($this->replyto_name) {
425 $name = $this->convertName($this->replyto_name);
426 $this->add_header("Reply-To: $name <$this->replyto_email>");
427 } else {
428 $this->add_header("Reply-To: $this->replyto_email");
429 }
430 }
431 // Organisation
432 if ($this->organisation) {
433 $name = $this->convertName($this->organisation);
434 $this->add_header("Organisation: $name");
435 }
436 // mailer
437 if ($this->mailer) {
438 $this->add_header("X-Mailer: $this->mailer");
439 }
440 // priority
441 if ($this->priority) {
442 $this->add_header("X-Priority: $this->priority");
443 }
444 $this->add_header("Mime-Version: 1.0");
445 }
446
447 /**
448 * [Describe function...]
449 *
450 * @param [type] $recip: ...
451 * @return [type] ...
452 */
453 function setRecipient ($recip) {
454 // Sets the recipient(s). If you supply a string, you set one recipient. If you supply an array, every value is added as a recipient.
455 if (is_array($recip)) {
456 $this->recipient = "";
457 while (list($key,) = each($recip)) {
458 $this->recipient .= $recip[$key].",";
459 }
460 $this->recipient = ereg_replace(",$","",$this->recipient);
461 } else {
462 $this->recipient = $recip;
463 }
464 }
465
466 /**
467 * [Describe function...]
468 *
469 * @return [type] ...
470 */
471 function getHTMLContentType() {
472 return count($this->theParts["html"]["media"]) ? 'multipart/related;' : 'multipart/alternative;';
473 }
474
475 /**
476 * [Describe function...]
477 *
478 * @return [type] ...
479 */
480 function setContent() {
481 // Begins building the message-body
482 $this->message = "";
483 $boundary = $this->getBoundary();
484 // Setting up headers
485 if (count($this->theParts["attach"])) {
486 $this->add_header('Content-Type: multipart/mixed;');
487 $this->add_header(' boundary="'.$boundary.'"');
488 $this->add_message("This is a multi-part message in MIME format.\n");
489 $this->constructMixed($boundary); // Generate (plain/HTML) / attachments
490 } elseif ($this->theParts["html"]["content"]) {
491 $this->add_header('Content-Type: '.$this->getHTMLContentType());
492 $this->add_header(' boundary="'.$boundary.'"');
493 $this->add_message("This is a multi-part message in MIME format.\n");
494 $this->constructHTML($boundary); // Generate plain/HTML mail
495 } else {
496 $this->add_header($this->plain_text_header);
497 $this->add_message($this->getContent("plain")); // Generate plain only
498 }
499 }
500
501 /**
502 * [Describe function...]
503 *
504 * @param [type] $boundary: ...
505 * @return [type] ...
506 */
507 function constructMixed ($boundary) {
508 // Here (plain/HTML) is combined with the attachments
509 $this->add_message("--".$boundary);
510 // (plain/HTML) is added
511 if ($this->theParts["html"]["content"]) {
512 // HTML and plain
513 $newBoundary = $this->getBoundary();
514 $this->add_message("Content-Type: ".$this->getHTMLContentType());
515 $this->add_message(' boundary="'.$newBoundary.'"');
516 $this->add_message('');
517 $this->constructHTML($newBoundary);
518 } else { // Purely plain
519 $this->add_message($this->plain_text_header);
520 $this->add_message('');
521 $this->add_message($this->getContent("plain"));
522 }
523 // attachments are added
524 if (is_array($this->theParts["attach"])) {
525 reset($this->theParts["attach"]);
526 while(list(,$media)=each($this->theParts["attach"])) {
527 $this->add_message("--".$boundary);
528 $this->add_message("Content-Type: ".$media["content_type"]);
529 $this->add_message(' name="'.$media["filename"].'"');
530 $this->add_message("Content-Transfer-Encoding: base64");
531 $this->add_message("Content-Disposition: attachment;");
532 $this->add_message(' filename="'.$media["filename"].'"');
533 $this->add_message('');
534 $this->add_message($this->makeBase64($media["content"]));
535 }
536 }
537 $this->add_message("--".$boundary."--\n");
538 }
539
540 /**
541 * [Describe function...]
542 *
543 * @param [type] $boundary: ...
544 * @return [type] ...
545 */
546 function constructHTML ($boundary) {
547 if (count($this->theParts["html"]["media"])) { // If media, then we know, the multipart/related content-type has been set before this function call...
548 $this->add_message("--".$boundary);
549 // HTML has media
550 $newBoundary = $this->getBoundary();
551 $this->add_message("Content-Type: multipart/alternative;");
552 $this->add_message(' boundary="'.$newBoundary.'"');
553 $this->add_message('');
554
555 $this->constructAlternative($newBoundary); // Adding the plaintext/html mix
556
557 $this->constructHTML_media($boundary);
558 } else {
559 $this->constructAlternative($boundary); // Adding the plaintext/html mix, and if no media, then use $boundary instead of $newBoundary
560 }
561 }
562
563 /**
564 * [Describe function...]
565 *
566 * @param [type] $boundary: ...
567 * @return [type] ...
568 */
569 function constructAlternative($boundary) {
570 // Here plain is combined with HTML
571 $this->add_message("--".$boundary);
572 // plain is added
573 $this->add_message($this->plain_text_header);
574 $this->add_message('');
575 $this->add_message($this->getContent("plain"));
576 $this->add_message("--".$boundary);
577 // html is added
578 $this->add_message($this->html_text_header);
579 $this->add_message('');
580 $this->add_message($this->getContent("html"));
581 $this->add_message("--".$boundary."--\n");
582 }
583
584 /**
585 * [Describe function...]
586 *
587 * @param [type] $boundary: ...
588 * @return [type] ...
589 */
590 function constructHTML_media ($boundary) {
591 /* // Constructs the HTML-part of message if the HTML contains media
592 $this->add_message("--".$boundary);
593 // htmlcode is added
594 $this->add_message($this->html_text_header);
595 $this->add_message('');
596 $this->add_message($this->getContent("html"));
597
598 OLD stuf...
599
600 */
601 // media is added
602 if (is_array($this->theParts["html"]["media"])) {
603 reset($this->theParts["html"]["media"]);
604 while(list($key,$media)=each($this->theParts["html"]["media"])) {
605 if (!$this->mediaList || t3lib_div::inList($this->mediaList,$key)) {
606 $this->add_message("--".$boundary);
607 $this->add_message("Content-Type: ".$media["ctype"]);
608 $this->add_message("Content-ID: <part".$key.".".$this->messageid.">");
609 $this->add_message("Content-Transfer-Encoding: base64");
610 $this->add_message('');
611 $this->add_message($this->makeBase64($media["content"]));
612 }
613 }
614 }
615 $this->add_message("--".$boundary."--\n");
616 }
617
618 /**
619 * Sends the mail by calling the mail() function in php. On Linux systems this will invoke the MTA
620 * defined in php.ini (sendmail -t -i by default), on Windows a SMTP must be specified in the sys.ini.
621 * Most common MTA's on Linux has a Sendmail interface, including Postfix and Exim.
622 * For setting the return-path correctly, the parameter -f has to be added to the system call to sendmail.
623 * This obviously does not have any effect on Windows, but on Sendmail compliant systems this works. If safe mode
624 * is enabled, then extra parameters is not allowed, so a safe mode check is made before the mail() command is
625 * invoked. When using the -f parameter, some MTA's will put an X-AUTHENTICATION-WARING saying that
626 * the return path was modified manually with the -f flag. To disable this warning make sure that the user running
627 * Apache is in the /etc/mail/trusted-users table.
628 *
629 * POSTFIX: With postfix version below 2.0 there is a problem that the -f parameter can not be used in conjunction
630 * with -t. Postfix will give an error in the maillog:
631 *
632 * cannot handle command-line recipients with -t
633 *
634 * This problem is solved by making a call to /usr/sbin/postconf mail_version and checking if the postfix is version
635 * 1.XX.YY If this is the case, the -f parameter is not used in the call to mail().
636 *
637 * This whole problem of return-path turns out to be quite tricky. If you have a solution that works better, on all
638 * standard MTA's then we are very open for suggestions.
639 *
640 * With time this function should be made such that several ways of sending the mail is possible (local MTA, smtp other).
641 *
642 *
643 * @return [type] ...
644 */
645 function sendTheMail () {
646 #debug(array($this->recipient,$this->subject,$this->message,$this->headers));
647 // Sends the mail, requires the recipient, message and headers to be set.
648 if (trim($this->recipient) && trim($this->message)) { // && trim($this->headers)
649 $returnPath = (strlen($this->returnPath)>0)?"-f".$this->returnPath:'';
650 //On windows the -f flag is not used (specific for Sendmail and Postfix), but instead the php.ini parameter sendmail_from is used.
651 if($this->returnPath) {
652 ini_set(sendmail_from, $this->returnPath);
653 }
654 //If safe mode is on, the fifth parameter to mail is not allowed, so the fix wont work on unix with safe_mode=On
655 if(!ini_get('safe_mode') && !$this->postfix_version1) {
656 mail($this->recipient,
657 $this->subject,
658 $this->message,
659 $this->headers,
660 $returnPath);
661 } else {
662 mail($this->recipient,
663 $this->subject,
664 $this->message,
665 $this->headers);
666 }
667 // Sending copy:
668 if ($this->recipient_copy) {
669 if(!ini_get('safe_mode') && !$this->postfix_version1) {
670 mail( $this->recipient_copy,
671 $this->subject,
672 $this->message,
673 $this->headers,
674 $returnPath);
675 } else {
676 mail( $this->recipient_copy,
677 $this->subject,
678 $this->message,
679 $this->headers );
680 }
681 }
682 // Auto response
683 if ($this->auto_respond_msg) {
684 $theParts = explode('/',$this->auto_respond_msg,2);
685 $theParts[1] = str_replace("/",chr(10),$theParts[1]);
686 if(!ini_get('safe_mode') && !$this->postfix_version1) {
687 mail( $this->from_email,
688 $theParts[0],
689 $theParts[1],
690 "From: ".$this->recipient,
691 $returnPath);
692 } else {
693 mail( $this->from_email,
694 $theParts[0],
695 $theParts[1],
696 "From: ".$this->recipient);
697 }
698 }
699 if($this->returnPath) {
700 ini_restore(sendmail_from);
701 }
702 return true;
703 } else {return false;}
704 }
705
706 /**
707 * [Describe function...]
708 *
709 * @return [type] ...
710 */
711 function getBoundary() {
712 // Returns boundaries
713 $this->part++;
714 return "----------".uniqid("part_".$this->part."_");
715 }
716
717 /**
718 * [Describe function...]
719 *
720 * @param [type] $content: ...
721 * @return [type] ...
722 */
723 function setPlain ($content) {
724 // Sets the plain-text part. No processing done.
725 $this->theParts["plain"]["content"] = $content;
726 }
727
728 /**
729 * [Describe function...]
730 *
731 * @param [type] $content: ...
732 * @return [type] ...
733 */
734 function setHtml ($content) {
735 // Sets the HTML-part. No processing done.
736 $this->theParts["html"]["content"] = $content;
737 }
738
739 /**
740 * [Describe function...]
741 *
742 * @param [type] $header: ...
743 * @return [type] ...
744 */
745 function add_header ($header) {
746 // Adds a header to the mail. Use this AFTER the setHeaders()-function
747 $this->headers.=$header."\n";
748 }
749
750 /**
751 * [Describe function...]
752 *
753 * @param [type] $string: ...
754 * @return [type] ...
755 */
756 function add_message ($string) {
757 // Adds a line of text to the mail-body. Is normally use internally
758 $this->message.=$string."\n";
759 }
760
761 /**
762 * [Describe function...]
763 *
764 * @param [type] $type: ...
765 * @return [type] ...
766 */
767 function getContent($type) {
768 return $this->theParts[$type]["content"];
769 }
770
771 /**
772 * [Describe function...]
773 *
774 * @return [type] ...
775 */
776 function preview() {
777 echo nl2br(HTMLSpecialChars($this->headers));
778 echo "<BR>";
779 echo nl2br(HTMLSpecialChars($this->message));
780 }
781
782
783
784
785
786
787
788
789
790
791
792
793
794 /****************************************************
795 *
796 * Functions for acquiring attachments, HTML, analyzing and so on **
797 *
798 ***************************************************/
799
800 /**
801 * @param [type] $file: ...
802 * @return [type] ...
803 */
804 function fetchHTML($file) {
805 // Fetches the HTML-content from either url og local serverfile
806 $this->theParts["html"]["content"] = $this->getURL($file); // Fetches the content of the page
807 if ($this->theParts["html"]["content"]) {
808 $addr = $this->extParseUrl($file);
809 $path = ($addr["scheme"]) ? $addr["scheme"]."://".$addr["host"].(($addr["filepath"])?$addr["filepath"]:"/") : $addr["filepath"];
810 $this->theParts["html"]["path"] = $path;
811 return true;
812 } else {
813 return false;
814 }
815 }
816
817 /**
818 * [Describe function...]
819 *
820 * @return [type] ...
821 */
822 function fetchHTMLMedia() {
823 // Fetches the mediafiles which are found by extractMediaLinks()
824 if (is_array($this->theParts["html"]["media"])) {
825 reset ($this->theParts["html"]["media"]);
826 if (count($this->theParts["html"]["media"]) > 0) {
827 while (list($key,$media) = each ($this->theParts["html"]["media"])) {
828 $picdata = $this->getExtendedURL($this->theParts["html"]["media"][$key]["absRef"]); // We fetch the content and the mime-type
829 if (is_array($picdata)) {
830 $this->theParts["html"]["media"][$key]["content"] = $picdata["content"];
831 $this->theParts["html"]["media"][$key]["ctype"] = $picdata["content_type"];
832 }
833 }
834 }
835 }
836 }
837
838 /**
839 * [Describe function...]
840 *
841 * @return [type] ...
842 */
843 function extractMediaLinks() {
844 // extracts all media-links from $this->theParts["html"]["content"]
845 $html_code = $this->theParts["html"]["content"];
846 $attribRegex = $this->tag_regex(Array("img","table","td","tr","body","iframe","script","input","embed"));
847 $codepieces = split($attribRegex, $html_code); // Splits the document by the beginning of the above tags
848 $len=strlen($codepieces[0]);
849 $pieces = count($codepieces);
850 for($i=1; $i < $pieces; $i++) {
851 $tag = strtolower(strtok(substr($html_code,$len+1,10)," "));
852 $len+=strlen($tag)+strlen($codepieces[$i])+2;
853 $dummy = eregi("[^>]*", $codepieces[$i], $reg);
854 $attributes = $this->get_tag_attributes($reg[0]); // Fetches the attributes for the tag
855 $imageData=array();
856 $imageData["ref"]=($attributes["src"]) ? $attributes["src"] : $attributes["background"]; // Finds the src or background attribute
857 if ($imageData["ref"]) {
858 $imageData["quotes"]=(substr($codepieces[$i],strpos($codepieces[$i], $imageData["ref"])-1,1)=='"')?'"':''; // Finds out if the value had quotes around it
859 $imageData["subst_str"] = $imageData["quotes"].$imageData["ref"].$imageData["quotes"]; // subst_str is the string to look for, when substituting lateron
860 if ($imageData["ref"] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
861 $this->image_fullpath_list.="|".$imageData["subst_str"]."|";
862 $imageData["absRef"] = $this->absRef($imageData["ref"]);
863 $imageData["tag"]=$tag;
864 $imageData["use_jumpurl"]=$attributes["dmailerping"]?1:0;
865 $this->theParts["html"]["media"][]=$imageData;
866 }
867 }
868 }
869 // Extracts stylesheets
870 $attribRegex = $this->tag_regex(Array("link"));
871 $codepieces = split($attribRegex, $html_code); // Splits the document by the beginning of the above tags
872 $pieces = count($codepieces);
873 for($i=1; $i < $pieces; $i++) {
874 $dummy = eregi("[^>]*", $codepieces[$i], $reg);
875 $attributes = $this->get_tag_attributes($reg[0]); // Fetches the attributes for the tag
876 $imageData=array();
877 if (strtolower($attributes["rel"])=="stylesheet" && $attributes["href"]) {
878 $imageData["ref"]=$attributes["href"]; // Finds the src or background attribute
879 $imageData["quotes"]=(substr($codepieces[$i],strpos($codepieces[$i], $imageData["ref"])-1,1)=='"')?'"':''; // Finds out if the value had quotes around it
880 $imageData["subst_str"] = $imageData["quotes"].$imageData["ref"].$imageData["quotes"]; // subst_str is the string to look for, when substituting lateron
881 if ($imageData["ref"] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
882 $this->image_fullpath_list.="|".$imageData["subst_str"]."|";
883 $imageData["absRef"] = $this->absRef($imageData["ref"]);
884 $this->theParts["html"]["media"][]=$imageData;
885 }
886 }
887 }
888 // fixes javascript rollovers
889 $codepieces = split(quotemeta(".src"), $html_code);
890 $pieces = count($codepieces);
891 $expr = "^[^".quotemeta("\"").quotemeta("'")."]*";
892 for($i=1; $i < $pieces; $i++) {
893 $temp = $codepieces[$i];
894 $temp = trim(ereg_replace("=","",trim($temp)));
895 ereg ($expr,substr($temp,1,strlen($temp)),$reg);
896 $imageData["ref"] = $reg[0];
897 $imageData["quotes"] = substr($temp,0,1);
898 $imageData["subst_str"] = $imageData["quotes"].$imageData["ref"].$imageData["quotes"]; // subst_str is the string to look for, when substituting lateron
899 $theInfo = $this->split_fileref($imageData["ref"]);
900 switch ($theInfo["fileext"]) {
901 case "gif":
902 case "jpeg":
903 case "jpg":
904 if ($imageData["ref"] && !strstr($this->image_fullpath_list,"|".$imageData["subst_str"]."|")) {
905 $this->image_fullpath_list.="|".$imageData["subst_str"]."|";
906 $imageData["absRef"] = $this->absRef($imageData["ref"]);
907 $this->theParts["html"]["media"][]=$imageData;
908 }
909 break;
910 }
911 }
912 }
913
914 /**
915 * [Describe function...]
916 *
917 * @return [type] ...
918 */
919 function extractHyperLinks() {
920 // extracts all hyper-links from $this->theParts["html"]["content"]
921 $html_code = $this->theParts["html"]["content"];
922 $attribRegex = $this->tag_regex(Array("a","form","area"));
923 $codepieces = split($attribRegex, $html_code); // Splits the document by the beginning of the above tags
924 $len=strlen($codepieces[0]);
925 $pieces = count($codepieces);
926 for($i=1; $i < $pieces; $i++) {
927 $tag = strtolower(strtok(substr($html_code,$len+1,10)," "));
928 $len+=strlen($tag)+strlen($codepieces[$i])+2;
929
930 $dummy = eregi("[^>]*", $codepieces[$i], $reg);
931 $attributes = $this->get_tag_attributes($reg[0]); // Fetches the attributes for the tag
932 $hrefData="";
933 if ($attributes["href"]) {$hrefData["ref"]=$attributes["href"];} else {$hrefData["ref"]=$attributes["action"];}
934 if ($hrefData["ref"]) {
935 $hrefData["quotes"]=(substr($codepieces[$i],strpos($codepieces[$i], $hrefData["ref"])-1,1)=='"')?'"':''; // Finds out if the value had quotes around it
936 $hrefData["subst_str"] = $hrefData["quotes"].$hrefData["ref"].$hrefData["quotes"]; // subst_str is the string to look for, when substituting lateron
937 if ($hrefData["ref"] && substr(trim($hrefData["ref"]),0,1)!="#" && !strstr($this->href_fullpath_list,"|".$hrefData["subst_str"]."|")) {
938 $this->href_fullpath_list.="|".$hrefData["subst_str"]."|";
939 $hrefData["absRef"] = $this->absRef($hrefData["ref"]);
940 $hrefData["tag"]=$tag;
941 $this->theParts["html"]["hrefs"][]=$hrefData;
942 }
943 }
944 }
945 // Extracts TYPO3 specific links made by the openPic() JS function
946 $codepieces = explode("onClick=\"openPic('", $html_code);
947 $pieces = count($codepieces);
948 for($i=1; $i < $pieces; $i++) {
949 $showpic_linkArr = explode("'",$codepieces[$i]);
950 $hrefData["ref"]=$showpic_linkArr[0];
951 if ($hrefData["ref"]) {
952 $hrefData["quotes"]="'"; // Finds out if the value had quotes around it
953 $hrefData["subst_str"] = $hrefData["quotes"].$hrefData["ref"].$hrefData["quotes"]; // subst_str is the string to look for, when substituting lateron
954 if ($hrefData["ref"] && !strstr($this->href_fullpath_list,"|".$hrefData["subst_str"]."|")) {
955 $this->href_fullpath_list.="|".$hrefData["subst_str"]."|";
956 $hrefData["absRef"] = $this->absRef($hrefData["ref"]);
957 $this->theParts["html"]["hrefs"][]=$hrefData;
958 }
959 }
960 }
961 }
962
963 /**
964 * [Describe function...]
965 *
966 * @return [type] ...
967 */
968 function extractFramesInfo() {
969 // extracts all media-links from $this->theParts["html"]["content"]
970 $html_code = $this->theParts["html"]["content"];
971 if (strpos(" ".$html_code,"<frame ")) {
972 $attribRegex = $this->tag_regex("frame");
973 $codepieces = split($attribRegex, $html_code, 1000000 ); // Splits the document by the beginning of the above tags
974 $pieces = count($codepieces);
975 for($i=1; $i < $pieces; $i++) {
976 $dummy = eregi("[^>]*", $codepieces[$i], $reg);
977 $attributes = $this->get_tag_attributes($reg[0]); // Fetches the attributes for the tag
978 $frame="";
979 $frame["src"]=$attributes["src"];
980 $frame["name"]=$attributes["name"];
981 $frame["absRef"] = $this->absRef($frame["src"]);
982 $theInfo[] = $frame;
983 }
984 return $theInfo;
985 }
986 }
987
988 /**
989 * [Describe function...]
990 *
991 * @param [type] $absolute: ...
992 * @return [type] ...
993 */
994 function substMediaNamesInHTML($absolute) {
995 // This substitutes the media-references in $this->theParts["html"]["content"]
996 // If $absolute is true, then the refs are substituted with http:// ref's indstead of Content-ID's (cid).
997 if (is_array($this->theParts["html"]["media"])) {
998 reset ($this->theParts["html"]["media"]);
999 while (list($key,$val) = each ($this->theParts["html"]["media"])) {
1000 if ($val["use_jumpurl"] && $this->jumperURL_prefix) {
1001 $theSubstVal = $this->jumperURL_prefix.rawurlencode($val["absRef"]);
1002 } else {
1003 $theSubstVal = ($absolute) ? $val["absRef"] : "cid:part".$key.".".$this->messageid;
1004 }
1005 $this->theParts["html"]["content"] = str_replace(
1006 $val["subst_str"],
1007 $val["quotes"].$theSubstVal.$val["quotes"],
1008 $this->theParts["html"]["content"] );
1009 }
1010 }
1011 if (!$absolute) {
1012 $this->fixRollOvers();
1013 }
1014 }
1015
1016 /**
1017 * [Describe function...]
1018 *
1019 * @return [type] ...
1020 */
1021 function substHREFsInHTML() {
1022 // This substitutes the hrefs in $this->theParts["html"]["content"]
1023 if (is_array($this->theParts["html"]["hrefs"])) {
1024 reset ($this->theParts["html"]["hrefs"]);
1025 while (list($key,$val) = each ($this->theParts["html"]["hrefs"])) {
1026 if ($this->jumperURL_prefix && $val["tag"]!="form") { // Form elements cannot use jumpurl!
1027 if ($this->jumperURL_useId) {
1028 $theSubstVal = $this->jumperURL_prefix.$key;
1029 } else {
1030 $theSubstVal = $this->jumperURL_prefix.rawurlencode($val["absRef"]);
1031 }
1032 } else {
1033 $theSubstVal = $val["absRef"];
1034 }
1035 $this->theParts["html"]["content"] = str_replace(
1036 $val["subst_str"],
1037 $val["quotes"].$theSubstVal.$val["quotes"],
1038 $this->theParts["html"]["content"] );
1039 }
1040 }
1041 }
1042
1043 /**
1044 * [Describe function...]
1045 *
1046 * @param [type] $content: ...
1047 * @return [type] ...
1048 */
1049 function substHTTPurlsInPlainText($content) {
1050 // This substitutes the http:// urls in plain text with links
1051 if ($this->jumperURL_prefix) {
1052 $textpieces = explode("http://", $content);
1053 $pieces = count($textpieces);
1054 $textstr = $textpieces[0];
1055 for($i=1; $i<$pieces; $i++) {
1056 $len=strcspn($textpieces[$i],chr(32).chr(9).chr(13).chr(10));
1057 if (trim(substr($textstr,-1))=="" && $len) {
1058 $lastChar=substr($textpieces[$i],$len-1,1);
1059 if (!ereg("[A-Za-z0-9\/#]",$lastChar)) {$len--;} // Included "\/" 3/12
1060
1061 $parts[0]="http://".substr($textpieces[$i],0,$len);
1062 $parts[1]=substr($textpieces[$i],$len);
1063
1064 if ($this->jumperURL_useId) {
1065 $this->theParts["plain"]["link_ids"][$i]=$parts[0];
1066 $parts[0] = $this->jumperURL_prefix."-".$i;
1067 } else {
1068 $parts[0] = $this->jumperURL_prefix.rawurlencode($parts[0]);
1069 }
1070 // debug($parts);
1071 $textstr.=$parts[0].$parts[1];
1072 } else {
1073 $textstr.='http://'.$textpieces[$i];
1074 }
1075 }
1076 $content = $textstr;
1077 // debug(array($textstr));
1078 // debug(md5($textstr));
1079 // debug(md5($content));
1080 }
1081 return $content;
1082 }
1083
1084 /**
1085 * [Describe function...]
1086 *
1087 * @return [type] ...
1088 */
1089 function fixRollOvers() {
1090 // JavaScript rollOvers cannot support graphics inside of mail. If these exists we must let them refer to the absolute url. By the way: Roll-overs seems to work only on some mail-readers and so far I've seen it work on Netscape 4 message-center (but not 4.5!!)
1091 $theNewContent = "";
1092 $theSplit = explode(".src",$this->theParts["html"]["content"]);
1093 if (count($theSplit)>1) {
1094 while(list($key,$part)=each($theSplit)) {
1095 $sub = substr($part,0,200);
1096 if (ereg("cid:part[^ \"']*",$sub,$reg)) {
1097 $thePos = strpos($part,$reg[0]); // The position of the string
1098 ereg("cid:part([^\.]*).*",$sub,$reg2); // Finds the id of the media...
1099 $theSubStr = $this->theParts["html"]["media"][intval($reg2[1])]["absRef"];
1100 if ($thePos && $theSubStr) { // ... and substitutes the javaScript rollover image with this instead
1101 if (!strpos(" ".$theSubStr,"http://")) {$theSubStr = "http://";} // If the path is NOT and url, the reference is set to nothing
1102 $part = substr($part,0,$thePos).$theSubStr.substr($part,$thePos+strlen($reg[0]),strlen($part));
1103 }
1104 }
1105 $theNewContent.= $part.((($key+1)!=count($theSplit))? ".src" : "" );
1106 }
1107 $this->theParts["html"]["content"]=$theNewContent;
1108 }
1109 }
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126 /*******************************************
1127 *
1128 * File and URL-functions
1129 *
1130 *******************************************/
1131
1132 /**
1133 * @param [type] $inputstr: ...
1134 * @return [type] ...
1135 */
1136 function makeBase64($inputstr) {
1137 // Returns base64-encoded content, which is broken every 76 character
1138 return chunk_split(base64_encode($inputstr));
1139 }
1140
1141 /**
1142 * [Describe function...]
1143 *
1144 * @param [type] $url: ...
1145 * @return [type] ...
1146 */
1147 function getExtendedURL($url) {
1148 // reads the URL or file and determines the Content-type by either guessing or opening a connection to the host
1149 $res["content"] = $this->getURL($url);
1150 if (!$res["content"]) {return false;}
1151 $pathInfo = parse_url($url);
1152 $fileInfo = $this->split_fileref($pathInfo["path"]);
1153 if ($fileInfo["fileext"] == "gif") {$res["content_type"] = "image/gif";}
1154 if ($fileInfo["fileext"] == "jpg" || $fileInfo["fileext"] == "jpeg") {$res["content_type"] = "image/jpeg";}
1155 if ($fileInfo["fileext"] == "html" || $fileInfo["fileext"] == "htm") {$res["content_type"] = "text/html";}
1156 if ($fileInfo["fileext"] == "swf") {$res["content_type"] = "application/x-shockwave-flash";}
1157 if (!$res["content_type"]) {$res["content_type"] = $this->getMimeType($url);}
1158 return $res;
1159 }
1160
1161 /**
1162 * [Describe function...]
1163 *
1164 * @param [type] $url: ...
1165 * @return [type] ...
1166 */
1167 function addUserPass($url) {
1168 $user=$this->http_username;
1169 $pass=$this->http_password;
1170 if ($user && $pass && substr($url,0,7)=="http://") {
1171 $url = "http://".$user.":".$pass."@".substr($url,7);
1172 }
1173 return $url;
1174 }
1175
1176 /**
1177 * [Describe function...]
1178 *
1179 * @param [type] $url: ...
1180 * @return [type] ...
1181 */
1182 function getURL($url) {
1183 $url = $this->addUserPass($url);
1184 // reads a url or file
1185 if($fd = @fopen($url,"rb")) {
1186 $content = "";
1187 while (!feof($fd)) {
1188 $content.=fread( $fd, 5000 );
1189 }
1190 fclose( $fd );
1191 return $content;
1192 } else {
1193 return false;
1194 }
1195 }
1196
1197 /**
1198 * [Describe function...]
1199 *
1200 * @param [type] $url: ...
1201 * @return [type] ...
1202 */
1203 function getStrippedURL($url) {
1204 // reads a url or file and strips the HTML-tags AND removes all empty lines. This is used to read plain-text out of a HTML-page
1205 if($fd = fopen($url,"rb")) {
1206 $content = "";
1207 while (!feof($fd)) {
1208 $line = fgetss($fd, 5000);
1209 if (trim($line)) {
1210 $content.=trim($line)."\n";
1211 }
1212 }
1213 fclose( $fd );
1214 return $content;
1215 }
1216 }
1217
1218 /**
1219 * [Describe function...]
1220 *
1221 * @param [type] $url: ...
1222 * @return [type] ...
1223 */
1224 function getMimeType($url) {
1225 // Opens a connection to the server and returns the mime-type of the file
1226 // takes url only
1227 $pathInfo = parse_url($url);
1228 if (!$pathInfo["scheme"]) {return false;}
1229 $getAdr = ($pathInfo["query"])?$pathInfo["path"]."?".$pathInfo["query"]:$pathInfo["path"];
1230 $fp = fsockopen($pathInfo["host"], 80, $errno, $errstr);
1231 if(!$fp) {
1232 return false;
1233 } else {
1234 fputs($fp,"GET ".$getAdr." HTTP/1.0\n\n");
1235 while(!feof($fp)) {
1236 $thePortion= fgets($fp,128);
1237 if (eregi("(^Content-Type: )(.*)",trim($thePortion), $reg)) {
1238 $res = trim($reg[2]);
1239 break;
1240 }
1241 }
1242 fclose($fp);
1243 }
1244 return $res;
1245 }
1246
1247 /**
1248 * [Describe function...]
1249 *
1250 * @param [type] $ref: ...
1251 * @return [type] ...
1252 */
1253 function absRef($ref) {
1254 // Returns the absolute address of a link. This is based on $this->theParts["html"]["path"] being the root-address
1255 $ref = trim($ref);
1256 $urlINFO = parse_url($ref);
1257 if ($urlINFO["scheme"]) {
1258 return $ref;
1259 } elseif (eregi("^/",$ref)){
1260 $addr = parse_url($this->theParts["html"]["path"]);
1261 return $addr["scheme"]."://".$addr["host"].$ref;
1262 } else {
1263 return $this->theParts["html"]["path"].$ref; // If the reference is relative, the path is added, in order for us to fetch the content
1264 }
1265 }
1266
1267 /**
1268 * [Describe function...]
1269 *
1270 * @param [type] $fileref: ...
1271 * @return [type] ...
1272 */
1273 function split_fileref($fileref) {
1274 // Returns an array with path, filename, filebody, fileext.
1275 if ( ereg("(.*/)(.*)$",$fileref,$reg) ) {
1276 $info["path"] = $reg[1];
1277 $info["file"] = $reg[2];
1278 } else {
1279 $info["path"] = "";
1280 $info["file"] = $fileref;
1281 }
1282 $reg="";
1283 if ( ereg("(.*)\.([^\.]*$)",$info["file"],$reg) ) {
1284 $info["filebody"] = $reg[1];
1285 $info["fileext"] = strtolower($reg[2]);
1286 $info["realFileext"] = $reg[2];
1287 } else {
1288 $info["filebody"] = $info["file"];
1289 $info["fileext"] = "";
1290 }
1291 return $info;
1292 }
1293
1294 /**
1295 * [Describe function...]
1296 *
1297 * @param [type] $path: ...
1298 * @return [type] ...
1299 */
1300 function extParseUrl($path) {
1301 // Returns an array with file or url-information
1302 $res = parse_url($path);
1303 ereg("(.*/)([^/]*)$",$res["path"],$reg);
1304 $res["filepath"]=$reg[1];
1305 $res["filename"]=$reg[2];
1306 return $res;
1307 }
1308
1309 /**
1310 * [Describe function...]
1311 *
1312 * @param [type] $tagArray: ...
1313 * @return [type] ...
1314 */
1315 function tag_regex($tagArray) {
1316 if (!is_array($tagArray)) {
1317 $tagArray=Array($tagArray);
1318 }
1319 $theRegex = "";
1320 $c=count($tagArray);
1321 while(list(,$tag)=each($tagArray)) {
1322 $c--;
1323 $theRegex.="<".sql_regcase($tag)."[[:space:]]".(($c)?"|":"");
1324 }
1325 return $theRegex;
1326 }
1327
1328 /**
1329 * analyses a HTML-tag
1330 * $tag is either like this "<TAG OPTION ATTRIB=VALUE>" or this " OPTION ATTRIB=VALUE>" which means you can omit the tag-name
1331 * returns an array with the attributes as keys in lower-case
1332 * If an attribute is empty (like OPTION) the value of that key is just empty. Check it with is_set();
1333 *
1334 * @param [type] $tag: ...
1335 * @return [type] ...
1336 */
1337 function get_tag_attributes($tag) {
1338 $attributes = Array();
1339 $tag = ltrim(eregi_replace ("^<[^ ]*","",trim($tag)));
1340 $tagLen = strlen($tag);
1341 $safetyCounter = 100;
1342 // Find attribute
1343 while ($tag) {
1344 $value = "";
1345 $reg = split("[[:space:]=>]",$tag,2);
1346 $attrib = $reg[0];
1347
1348 $tag = ltrim(substr($tag,strlen($attrib),$tagLen));
1349 if (substr($tag,0,1)=="=") {
1350 $tag = ltrim(substr($tag,1,$tagLen));
1351 if (substr($tag,0,1)=='"') { // Quotes around the value
1352 $reg = explode('"',substr($tag,1,$tagLen),2);
1353 $tag = ltrim($reg[1]);
1354 $value = $reg[0];
1355 } else { // No qoutes around value
1356 ereg("^([^[:space:]>]*)(.*)",$tag,$reg);
1357 $value = trim($reg[1]);
1358 $tag = ltrim($reg[2]);
1359 if (substr($tag,0,1)==">") {
1360 $tag ="";
1361 }
1362 }
1363 }
1364 $attributes[strtolower($attrib)]=$value;
1365 $safetyCounter--;
1366 if ($safetyCounter<0) {break;}
1367 }
1368 return $attributes;
1369 }
1370
1371 /**
1372 * [Describe function...]
1373 *
1374 * @param [type] $string: ...
1375 * @return [type] ...
1376 */
1377 function quoted_printable($string) {
1378 // This functions is buggy. It seems that in the part where the lines are breaked every 76th character, that it fails if the break happens right in a quoted_printable encode character!
1379 $newString = "";
1380 $theLines = explode(chr(10),$string); // Break lines. Doesn't work with mac eol's which seems to be 13. But 13-10 or 10 will work
1381 while (list(,$val)=each($theLines)) {
1382 $val = ereg_replace(chr(13)."$","",$val); // removes possible character 13 at the end of line
1383
1384 $newVal = "";
1385 $theValLen = strlen($val);
1386 $len = 0;
1387 for ($index=0;$index<$theValLen;$index++) {
1388 $char = substr($val,$index,1);
1389 $ordVal =Ord($char);
1390 if ($len>(76-4) || ($len>(66-4)&&$ordVal==32)) {
1391 $len=0;
1392 $newVal.="=".chr(13).chr(10);
1393 }
1394 if (($ordVal>=33 && $ordVal<=60) || ($ordVal>=62 && $ordVal<=126) || $ordVal==9 || $ordVal==32) {
1395 $newVal.=$char;
1396 $len++;
1397 } else {
1398 $newVal.=sprintf("=%02X",$ordVal);
1399 $len+=3;
1400 }
1401 }
1402 $newVal = ereg_replace(chr(32)."$","=20",$newVal); // replaces a possible SPACE-character at the end of a line
1403 $newVal = ereg_replace(chr(9)."$","=09",$newVal); // replaces a possible TAB-character at the end of a line
1404 $newString.=$newVal.chr(13).chr(10);
1405 }
1406 return $newString;
1407 }
1408
1409 /**
1410 * [Describe function...]
1411 *
1412 * @param [type] $name: ...
1413 * @return [type] ...
1414 */
1415 function convertName($name) {
1416 if (ereg("[^".chr(32)."-".chr(60).chr(62)."-".chr(127)."]",$name)) {
1417 return '=?iso-8859-1?B?'.base64_encode($name).'?=';
1418 } else {
1419 return $name;
1420 }
1421 }
1422 }
1423
1424 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_htmlmail.php']) {
1425 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_htmlmail.php']);
1426 }
1427 ?>