PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / site / class / email_message.php

email_message.php in VikBooking Hotel Booking Engine & PMS trunk, at site/class/email_message.php

3,549 lines 102.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 class email_message_class
14 {
15 /* Private variables */
16
17 var $headers=array("To"=>"","Subject"=>"");
18 var $body=-1;
19 var $body_parts=0;
20 var $parts=array();
21 var $total_parts=0;
22 var $free_parts=array();
23 var $total_free_parts=0;
24 var $delivery=array("State"=>"");
25 var $next_token="";
26 var $php_version=0;
27 var $mailings=array();
28 var $last_mailing=0;
29 var $header_length_limit=512;
30 var $auto_message_id=1;
31 var $mailing_path="";
32 var $body_cache=array();
33 var $line_break="\n";
34 var $line_length=75;
35 var $ruler="_";
36 var $email_address_pattern="([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}";
37 var $bulk_mail=0;
38
39 /* Public variables */
40
41 /*
42 {metadocument}
43 <variable>
44 <name>email_regular_expression</name>
45 <type>STRING</type>
46 <value>^([-!#$%&amp;'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#$%&amp;'*+/0-9=?A-Z^_`a-z{|}~]+\.)+[a-zA-Z]{2,6}$</value>
47 <documentation>
48 <purpose>Specify the regular expression that is used by the
49 <functionlink>ValidateEmailAddress</functionlink> function to
50 verify whether a given e-mail address may be valid.</purpose>
51 <usage>Do not change this variable unless you have reason to believe
52 that it is rejecting existing e-mail addresses that are known to be
53 valid.</usage>
54 </documentation>
55 </variable>
56 {/metadocument}
57 */
58 var $email_regular_expression="^([-!#\$%&'*+./0-9=?A-Z^_`a-z{|}~])+@([-!#\$%&'*+/0-9=?A-Z^_`a-z{|}~]+\\.)+[a-zA-Z]{2,6}\$";
59
60 /*
61 {metadocument}
62 <variable>
63 <name>mailer</name>
64 <type>STRING</type>
65 <value>http://www.phpclasses.org/mimemessage $Revision: 1.92 $</value>
66 <documentation>
67 <purpose>Specify the base text that is used identify the name and the
68 version of the class that is used to send the message by setting an
69 implicit the <tt>X-Mailer</tt> message header. This is meant
70 mostly to assist on the debugging of delivery problems.</purpose>
71 <usage>Change this to set another mailer identification string or
72 leave it to an empty string to prevent that the <tt>X-Mailer</tt>
73 header be added to the message.</usage>
74 </documentation>
75 </variable>
76 {/metadocument}
77 */
78 var $mailer='';
79
80 /*
81 {metadocument}
82 <variable>
83 <name>mailer_delivery</name>
84 <type>STRING</type>
85 <value>mail</value>
86 <documentation>
87 <purpose>Specify the text that is used to identify the mail
88 delivery class or sub-class. This text is appended to the
89 <tt>X-Mailer</tt> header text defined by the
90 <variablelink>mailer</variablelink> variable.</purpose>
91 <usage>This variable should only be redefined by the different mail
92 delivery sub-classes.</usage>
93 </documentation>
94 </variable>
95 {/metadocument}
96 */
97 var $mailer_delivery='mail';
98
99 /*
100 {metadocument}
101 <variable>
102 <name>default_charset</name>
103 <type>STRING</type>
104 <value>ISO-8859-1</value>
105 <documentation>
106 <purpose>Specify the default character set to be assumed for the
107 message headers and body text.</purpose>
108 <usage>Change this variable to the correct character set name if it
109 is different than the default.</usage>
110 </documentation>
111 </variable>
112 {/metadocument}
113 */
114 //var $default_charset="ISO-8859-1";
115 var $default_charset="UTF-8";
116
117 /*
118 {metadocument}
119 <variable>
120 <name>line_quote_prefix</name>
121 <type>STRING</type>
122 <value>&gt; </value>
123 <documentation>
124 <purpose>Specify the default line quote prefix text used by the
125 <functionlink>QuoteText</functionlink> function.</purpose>
126 <usage>Change it only if you prefer to quote lines marking them with
127 a different line prefix.</usage>
128 </documentation>
129 </variable>
130 {/metadocument}
131 */
132 var $line_quote_prefix="> ";
133
134 /*
135 {metadocument}
136 <variable>
137 <name>break_long_lines</name>
138 <type>BOOLEAN</type>
139 <value>1</value>
140 <documentation>
141 <purpose>Determine whether lines exceeding the length limit will be
142 broken by the line break character when using the
143 <functionlink>WrapText</functionlink> function.</purpose>
144 <usage>Change it only if you to avoid breaking long lines without
145 any space characters, like for instance of messages with long
146 URLs.</usage>
147 </documentation>
148 </variable>
149 {/metadocument}
150 */
151 var $break_long_lines=1;
152
153 /*
154 {metadocument}
155 <variable>
156 <name>file_buffer_length</name>
157 <type>INTEGER</type>
158 <value>8000</value>
159 <documentation>
160 <purpose>Specify the length of the buffer that is used to read
161 files in chunks of limited size.</purpose>
162 <usage>The default value may be increased if you have plenty of
163 memory and want to benefit from additional speed when processing
164 the files that are used to compose messages.</usage>
165 </documentation>
166 </variable>
167 {/metadocument}
168 */
169 var $file_buffer_length=8000;
170
171 /*
172 {metadocument}
173 <variable>
174 <name>debug</name>
175 <type>STRING</type>
176 <value></value>
177 <documentation>
178 <purpose>Specify the name of a function that is called whenever an
179 error occurs.</purpose>
180 <usage>If you need to track the errors that may happen during the use
181 of the class, set this variable to the name of a callback function.
182 It should take only one argument that is the error message. When
183 this variable is set to an empty string, no debug callback function
184 is called.</usage>
185 </documentation>
186 </variable>
187 {/metadocument}
188 */
189 var $debug="";
190
191 /*
192 {metadocument}
193 <variable>
194 <name>cache_body</name>
195 <type>BOOLEAN</type>
196 <value>0</value>
197 <documentation>
198 <purpose>Specify whether the message bodies that are generated by the
199 class before sending, should be cached in memory to be reused on
200 the next message delivery.</purpose>
201 <usage>Set this variable to <tt><booleanvalue>1</booleanvalue></tt>
202 if you intend to send the a message with the same body to many
203 recipients, so the class avoids the overhead of regenerating
204 messages with the same content.</usage>
205 </documentation>
206 </variable>
207 {/metadocument}
208 */
209 var $cache_body=0;
210
211 /*
212 {metadocument}
213 <variable>
214 <name>error</name>
215 <type>STRING</type>
216 <value></value>
217 <documentation>
218 <purpose>Store the last error return by any function that may fail
219 due to some error.</purpose>
220 <usage>Do not change this variable value unless you intend to clear
221 the error status by setting it to an empty string.</usage>
222 </documentation>
223 </variable>
224 {/metadocument}
225 */
226 var $error="";
227
228 /*
229 {metadocument}
230 <variable>
231 <name>localhost</name>
232 <type>STRING</type>
233 <value></value>
234 <documentation>
235 <purpose>Specify the domain name of the computer sending the
236 message.</purpose>
237 <usage>This value is used as default domain of the sender e-mail
238 address when generating automatic <tt>Message-Id</tt>
239 headers.</usage>
240 </documentation>
241 </variable>
242 {/metadocument}
243 */
244 var $localhost="";
245
246 /* Private methods */
247
248 Function Tokenize($string,$separator="")
249 {
250 if(!strcmp($separator,""))
251 {
252 $separator=$string;
253 $string=$this->next_token;
254 }
255 for($character=0;$character<strlen($separator);++$character)
256 {
257 if(GetType($position=strpos($string,$separator[$character]))=="integer")
258 $found=(IsSet($found) ? min($found,$position) : $position);
259 }
260 if(IsSet($found))
261 {
262 $this->next_token=substr($string,$found+1);
263 return(substr($string,0,$found));
264 }
265 else
266 {
267 $this->next_token="";
268 return($string);
269 }
270 }
271
272 Function GetFilenameExtension($filename)
273 {
274 return(GetType($dot=strrpos($filename,"."))=="integer" ? substr($filename,$dot) : "");
275 }
276
277 Function OutputError($error)
278 {
279 if(strcmp($function=$this->debug,"")
280 && strcmp($error,""))
281 $function($error);
282 return($this->error=$error);
283 }
284
285 Function OutputPHPError($error, &$php_error_message)
286 {
287 if(IsSet($php_error_message)
288 && strlen($php_error_message))
289 $error.=": ".$php_error_message;
290 return($this->OutputError($error));
291 }
292
293 Function GetPHPVersion()
294 {
295 if($this->php_version==0)
296 {
297 $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
298 $this->php_version=$version[0]*1000000+$version[1]*1000+$version[2];
299 }
300 return($this->php_version);
301 }
302
303 Function EscapePattern($pattern)
304 {
305 return('/'.str_replace('/', '\\/', $pattern).'/');
306 }
307
308 Function GetRFC822Addresses($address,&$addresses)
309 {
310 if(function_exists("imap_rfc822_parse_adrlist"))
311 {
312 if(GetType($parsed_addresses=@imap_rfc822_parse_adrlist($address,$this->localhost))!="array")
313 return("it was not specified a valid address list");
314 for($entry=0;$entry<count($parsed_addresses);++$entry)
315 {
316 if(!IsSet($parsed_addresses[$entry]->host)
317 || $parsed_addresses[$entry]->host==".SYNTAX-ERROR.")
318 return($parsed_addresses[$entry]->mailbox." .SYNTAX-ERROR.");
319 $parsed_address=$parsed_addresses[$entry]->mailbox."@".$parsed_addresses[$entry]->host;
320 if(IsSet($addresses[$parsed_address]))
321 ++$addresses[$parsed_address];
322 else
323 $addresses[$parsed_address]=1;
324 }
325 }
326 else
327 {
328 $length=strlen($address);
329 for($position=0;$position<$length;)
330 {
331 $match=preg_split($this->EscapePattern($this->email_address_pattern),strtolower(substr($address,$position)),2);
332 if(count($match)<2)
333 break;
334 $position+=strlen($match[0]);
335 $next_position=$length-strlen($match[1]);
336 $found=substr($address,$position,$next_position-$position);
337 if(!strcmp($found,""))
338 break;
339 if(IsSet($addresses[$found]))
340 ++$addresses[$found];
341 else
342 $addresses[$found]=1;
343 $position=$next_position;
344 }
345 }
346 return("");
347 }
348
349 Function FormatHeader($header_name,$header_value)
350 {
351 $length=strlen($header_value);
352 for($header_data="",$header_line=$header_name.": ",$line_length=strlen($header_line),$position=0;$position<$length;)
353 {
354 for($space=$position,$line_length=strlen($header_line);$space<$length;)
355 {
356 if(GetType($next=strpos($header_value," ",$space+1))!="integer")
357 $next=$length;
358 if($next-$position+$line_length>$this->header_length_limit)
359 {
360 if($space==$position)
361 $space=$next;
362 break;
363 }
364 $space=$next;
365 }
366 $header_data.=$header_line.substr($header_value,$position,$space-$position);
367 if($space<$length)
368 $header_line="";
369 $position=$space;
370 if($position<$length)
371 $header_data.=$this->line_break;
372 }
373 return($header_data);
374 }
375
376 Function GenerateMessageID($sender)
377 {
378 $micros=$this->Tokenize(microtime()," ");
379 $seconds=$this->Tokenize("");
380 $local=$this->Tokenize($sender,"@");
381 $host=$this->Tokenize(" @");
382 if(strlen($host)
383 && $host[strlen($host)-1]=="-")
384 $host=substr($host,0,strlen($host)-1);
385 return($this->FormatHeader("Message-ID", "<".strftime("%Y%m%d%H%M%S", $seconds).substr($micros,1,5).".".preg_replace('/[^A-Za-z]/', '-', $local)."@".preg_replace('/[^.A-Za-z_-]/', '', $host).">"));
386 }
387
388 Function SendMail($to,$subject,&$body,&$headers,$return_path)
389 {
390 if(!function_exists("mail"))
391 return($this->OutputError("the mail() function is not available in this PHP installation"));
392 if(strlen($return_path))
393 {
394 if(!defined("PHP_OS"))
395 return($this->OutputError("it is not possible to set the Return-Path header with your PHP version"));
396 if(!strcmp(substr(PHP_OS,0,3),"WIN"))
397 return($this->OutputError("it is not possible to set the Return-Path header directly from a PHP script on Windows"));
398 if($this->GetPHPVersion()<4000005)
399 return($this->OutputError("it is not possible to set the Return-Path header in PHP version older than 4.0.5"));
400 if(function_exists("ini_get")
401 && ini_get("safe_mode"))
402 return($this->OutputError("it is not possible to set the Return-Path header due to PHP safe mode restrictions"));
403 $success=@mail($to,$subject,$body,$headers,"-f".$return_path);
404 }
405 else
406 $success=@mail($to,$subject,$body,$headers);
407 return($success ? "" : $this->OutputPHPError("it was not possible to send e-mail message", $php_errormsg));
408 }
409
410 Function StartSendingMessage()
411 {
412 if(strcmp($this->delivery["State"],""))
413 return($this->OutputError("the message was already started to be sent"));
414 $this->delivery=array("State"=>"SendingHeaders");
415 return("");
416 }
417
418 Function SendMessageHeaders($headers)
419 {
420 if(strcmp($this->delivery["State"],"SendingHeaders"))
421 {
422 if(!strcmp($this->delivery["State"],""))
423 return($this->OutputError("the message was not yet started to be sent"));
424 else
425 return($this->OutputError("the message headers were already sent"));
426 }
427 $this->delivery["Headers"]=$headers;
428 $this->delivery["State"]="SendingBody";
429 return("");
430 }
431
432 Function SendMessageBody($data)
433 {
434 if(strcmp($this->delivery["State"],"SendingBody"))
435 return($this->OutputError("the message headers were not yet sent"));
436 if(IsSet($this->delivery["Body"]))
437 $this->delivery["Body"].=$data;
438 else
439 $this->delivery["Body"]=$data;
440 return("");
441 }
442
443 Function EndSendingMessage()
444 {
445 if(strcmp($this->delivery["State"],"SendingBody"))
446 return($this->OutputError("the message body data was not yet sent"));
447 if(!IsSet($this->delivery["Headers"])
448 || count($this->delivery["Headers"])==0)
449 return($this->OutputError("message has no headers"));
450 $line_break=((defined("PHP_OS") && !strcmp(substr(PHP_OS,0,3),"WIN")) ? "\r\n" : $this->line_break);
451 $headers=$this->delivery["Headers"];
452 for($has=array(),$headers_text="",$header=0,Reset($headers);$header<count($headers);Next($headers),++$header)
453 {
454 $header_name=Key($headers);
455 switch(strtolower($header_name))
456 {
457 case "to":
458 case "subject":
459 $has[strtolower($header_name)]=$headers[$header_name];
460 break;
461 case "cc":
462 case "bcc":
463 case "from":
464 case "return-path":
465 case "message-id":
466 $has[strtolower($header_name)]=$headers[$header_name];
467 default:
468 $header_line=$header_name.": ".$headers[$header_name];
469 if(strlen($headers_text))
470 $headers_text.=$this->line_break.$header_line;
471 else
472 $headers_text=$header_line;
473 }
474 }
475 if(strlen($has["to"])==0
476 && !IsSet($has["cc"])
477 && !IsSet($has["bcc"]))
478 return($this->OutputError("it were not specified a valid To:, Cc: or Bcc: headers"));
479 if(!IsSet($has["subject"]))
480 return($this->OutputError("it was not specified a valid Subject: header"));
481 if(!IsSet($has["message-id"])
482 && $this->auto_message_id)
483 {
484 $sender = array();
485 if(IsSet($has["return-path"]))
486 $sender[] = $has["return-path"];
487 if(IsSet($has["from"]))
488 $sender[] = $has["from"];
489 $sender[] = $has["to"];
490 $ts = count($sender);
491 for($s = 0; $s < $ts; ++$s)
492 {
493 $error = $this->GetRFC822Addresses($sender[$s], $senders);
494 if(strlen($error) == 0
495 && count($senders))
496 break;
497 }
498 if(count($senders) == 0)
499 return('it was not specified a valid sender address'.(strlen($error) ? ': '.$error : ''));
500 Reset($senders);
501 $sender=Key($senders);
502 $header_line=$this->GenerateMessageID($sender);
503 if(strlen($headers_text))
504 $headers_text.=$this->line_break.$header_line;
505 else
506 $headers_text=$header_line;
507 }
508 if(strcmp($error=$this->SendMail(strlen($has["to"]) ? $has["to"] : (IsSet($has["cc"]) ? "" : "undisclosed-recipients: ;"), $has["subject"], $this->delivery["Body"], $headers_text, IsSet($has["return-path"]) ? $has["return-path"] : ""),""))
509 return($error);
510 $this->delivery=array("State"=>"");
511 return("");
512 }
513
514 Function StopSendingMessage()
515 {
516 $this->delivery=array("State"=>"");
517 return("");
518 }
519
520 Function GetPartBoundary($part)
521 {
522 if(!IsSet($this->parts[$part]["BOUNDARY"]))
523 $this->parts[$part]["BOUNDARY"]=md5(uniqid($part.time()));
524 }
525
526 Function GetPartHeaders(&$headers,$part)
527 {
528 if(IsSet($this->parts[$part]['CachedHeaders']))
529 {
530 $headers = $this->parts[$part]['CachedHeaders'];
531 return('');
532 }
533 if(!IsSet($this->parts[$part]["Content-Type"]))
534 return($this->OutputError("it was added a part without Content-Type: defined"));
535 $type=$this->Tokenize($full_type=strtolower($this->parts[$part]["Content-Type"]),"/");
536 $sub_type=$this->Tokenize("");
537 switch($type)
538 {
539 case "text":
540 case "image":
541 case "audio":
542 case "video":
543 case "application":
544 case "message":
545 if(IsSet($this->parts[$part]["NAME"]))
546 $filename = $this->QuotedPrintableEncode($this->parts[$part]["NAME"], $this->default_charset, 1, 1);
547 $headers["Content-Type"]=$full_type.(IsSet($this->parts[$part]["CHARSET"]) ? "; charset=".$this->parts[$part]["CHARSET"] : "").(IsSet($this->parts[$part]["NAME"]) ? "; name=\"".$filename."\"" : "");
548 if(IsSet($this->parts[$part]["Content-Transfer-Encoding"]))
549 $headers["Content-Transfer-Encoding"]=$this->parts[$part]["Content-Transfer-Encoding"];
550 if(IsSet($this->parts[$part]["DISPOSITION"])
551 && strlen($this->parts[$part]["DISPOSITION"]))
552 $headers["Content-Disposition"]=$this->parts[$part]["DISPOSITION"].(IsSet($this->parts[$part]["NAME"]) ? "; filename=\"".$filename."\"" : "");
553 break;
554 case "multipart":
555 switch($sub_type)
556 {
557 case "alternative":
558 case "related":
559 case "mixed":
560 case "parallel":
561 $this->GetPartBoundary($part);
562 $headers["Content-Type"]=$full_type."; boundary=\"".$this->parts[$part]["BOUNDARY"]."\"";
563 break;
564 default:
565 return($this->OutputError("multipart Content-Type sub_type $sub_type not yet supported"));
566 }
567 break;
568 default:
569 return($this->OutputError("Content-Type: $full_type not yet supported"));
570 }
571 if(IsSet($this->parts[$part]["Content-ID"]))
572 $headers["Content-ID"]="<".$this->parts[$part]["Content-ID"].">";
573 if(IsSet($this->parts[$part]['Cache'])
574 && $this->parts[$part]['Cache'])
575 $this->parts[$part]['CachedHeaders'] = $headers;
576 return("");
577 }
578
579 Function GetPartBody(&$body,$part)
580 {
581 if(IsSet($this->parts[$part]['CachedBody']))
582 {
583 $body = $this->parts[$part]['CachedBody'];
584 return('');
585 }
586 if(!IsSet($this->parts[$part]["Content-Type"]))
587 return($this->OutputError("it was added a part without Content-Type: defined"));
588 $type=$this->Tokenize($full_type=strtolower($this->parts[$part]["Content-Type"]),"/");
589 $sub_type=$this->Tokenize("");
590 $body="";
591 switch($type)
592 {
593 case "text":
594 case "image":
595 case "audio":
596 case "video":
597 case "application":
598 case "message":
599 if(IsSet($this->parts[$part]["FILENAME"]))
600 {
601 $size=@filesize($this->parts[$part]["FILENAME"]);
602 if(!($file=@fopen($this->parts[$part]["FILENAME"],"rb")))
603 return($this->OutputPHPError("could not open part file ".$this->parts[$part]["FILENAME"], $php_errormsg));
604 while(!feof($file))
605 {
606 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
607 {
608 fclose($file);
609 return($this->OutputPHPError("could not read part file", $php_errormsg));
610 }
611 $body.=$block;
612 }
613 fclose($file);
614 if((GetType($size)=="integer"
615 && strlen($body)>$size)
616 || (function_exists("get_magic_quotes_runtime")
617 && get_magic_quotes_runtime()))
618 $body=StripSlashes($body);
619 if(GetType($size)=="integer"
620 && strlen($body)!=$size)
621 return($this->OutputError("the length of the file that was read does not match the size of the part file ".$this->parts[$part]["FILENAME"]." due to possible data corruption"));
622 }
623 else
624 {
625 if(!IsSet($this->parts[$part]["DATA"]))
626 return($this->OutputError("it was added a part without a body PART"));
627 $body=$this->parts[$part]["DATA"];
628 }
629 $encoding=(IsSet($this->parts[$part]["Content-Transfer-Encoding"]) ? strtolower($this->parts[$part]["Content-Transfer-Encoding"]) : "");
630 switch($encoding)
631 {
632 case "base64":
633 $body=chunk_split(base64_encode($body));
634 break;
635 case "":
636 case "quoted-printable":
637 case "7bit":
638 break;
639 default:
640 return($this->OutputError($encoding." is not yet a supported encoding type"));
641 }
642 break;
643 case "multipart":
644 switch($sub_type)
645 {
646 case "alternative":
647 case "related":
648 case "mixed":
649 case "parallel":
650 $this->GetPartBoundary($part);
651 $boundary=$this->line_break."--".$this->parts[$part]["BOUNDARY"];
652 $parts=count($this->parts[$part]["PARTS"]);
653 for($multipart=0;$multipart<$parts;$multipart++)
654 {
655 $body.=$boundary.$this->line_break;
656 $part_headers=array();
657 $sub_part=$this->parts[$part]["PARTS"][$multipart];
658 if(strlen($error=$this->GetPartHeaders($part_headers,$sub_part)))
659 return($error);
660 for($part_header=0,Reset($part_headers);$part_header<count($part_headers);$part_header++,Next($part_headers))
661 {
662 $header=Key($part_headers);
663 $body.=$header.": ".$part_headers[$header].$this->line_break;
664 }
665 $body.=$this->line_break;
666 if(strlen($error=$this->GetPartBody($part_body,$sub_part)))
667 return($error);
668 $body.=$part_body;
669 }
670 $body.=$boundary."--".$this->line_break;
671 break;
672 default:
673 return($this->OutputError("multipart Content-Type sub_type $sub_type not yet supported"));
674 }
675 break;
676 default:
677 return($this->OutputError("Content-Type: $full_type not yet supported"));
678 }
679 if(IsSet($this->parts[$part]['Cache'])
680 && $this->parts[$part]['Cache'])
681 $this->parts[$part]['CachedBody'] = $body;
682 return("");
683 }
684
685 /* Public functions */
686
687 /*
688 {metadocument}
689 <function>
690 <name>ValidateEmailAddress</name>
691 <type>BOOLEAN</type>
692 <documentation>
693 <purpose>Determine whether a given e-mail address may be
694 valid.</purpose>
695 <usage>Just pass the e-mail <argumentlink>
696 <function>ValidateEmailAddress</function>
697 <argument>address</argument>
698 </argumentlink> to be checked as function argument. This function
699 uses the regular expression defined by the
700 <variablelink>email_regular_expression</variablelink> variable to
701 check the address.</usage>
702 <returnvalue>The function returns
703 <tt><booleanvalue>1</booleanvalue></tt> if the specified address
704 may be valid.</returnvalue>
705 </documentation>
706 <argument>
707 <name>address</name>
708 <type>STRING</type>
709 <documentation>
710 <purpose>Specify the e-mail address to be validated.</purpose>
711 </documentation>
712 </argument>
713 <do>
714 {/metadocument}
715 */
716 Function ValidateEmailAddress($address)
717 {
718 return(preg_match('/'.str_replace('/', '\\/'. $this->email_regular_expression).'/i',$address));
719 }
720 /*
721 {metadocument}
722 </do>
723 </function>
724 {/metadocument}
725 */
726
727 Function QuotedPrintableEncode($text, $header_charset='', $break_lines=1, $email_header = 0)
728 {
729 $ln=strlen($text);
730 $h=(strlen($header_charset)>0);
731 if($h)
732 {
733 $encode = array(
734 '='=>1,
735 '?'=>1,
736 '_'=>1,
737 '('=>1,
738 ')'=>1,
739 '<'=>1,
740 '>'=>1,
741 '@'=>1,
742 ','=>1,
743 ';'=>1,
744 '"'=>1,
745 '\\'=>1,
746 '['=>1,
747 ']'=>1,
748 ':'=>1,
749 /*
750 '/'=>1,
751 '.'=>1,
752 */
753 );
754 $s=($email_header ? $encode : array());
755 $b=$space=$break_lines=0;
756 for($i=0; $i<$ln; ++$i)
757 {
758 $c = $text[$i];
759 if(IsSet($s[$c]))
760 {
761 $b=1;
762 break;
763 }
764 switch($o=Ord($c))
765 {
766 case 9:
767 case 32:
768 $space=$i+1;
769 $b=1;
770 break 2;
771 case 10:
772 case 13:
773 break 2;
774 default:
775 if($o<32
776 || $o>127)
777 {
778 $b=1;
779 $s = $encode;
780 break 2;
781 }
782 }
783 }
784 if($i==$ln)
785 return($text);
786 if($space>0)
787 return(substr($text,0,$space).($space<$ln ? $this->QuotedPrintableEncode(substr($text,$space), $header_charset, $break_lines, $email_header) : ""));
788 }
789 for($w=$e='',$n=0, $l=0,$i=0;$i<$ln; ++$i)
790 {
791 $c = $text[$i];
792 $o=Ord($c);
793 $en=0;
794 switch($o)
795 {
796 case 9:
797 case 32:
798 if(!$h)
799 {
800 $w=$c;
801 $c='';
802 }
803 else
804 {
805 if($b)
806 {
807 if($o==32)
808 $c='_';
809 else
810 $en=1;
811 }
812 }
813 break;
814 case 10:
815 case 13:
816 if(strlen($w))
817 {
818 if($break_lines
819 && $l+3>75)
820 {
821 $e.='='.$this->line_break;
822 $l=0;
823 }
824 $e.=sprintf('=%02X',Ord($w));
825 $l+=3;
826 $w='';
827 }
828 $e.=$c;
829 if($h)
830 $e.="\t";
831 $l=0;
832 continue 2;
833 case 46:
834 case 70:
835 case 102:
836 $en=(!$h && ($l==0 || $l+1>75));
837 break;
838 default:
839 if($o>127
840 || $o<32
841 || !strcmp($c,'='))
842 $en=1;
843 elseif($h
844 && IsSet($s[$c]))
845 $en=1;
846 break;
847 }
848 if(strlen($w))
849 {
850 if($break_lines
851 && $l+1>75)
852 {
853 $e.='='.$this->line_break;
854 $l=0;
855 }
856 $e.=$w;
857 ++$l;
858 $w='';
859 }
860 if(strlen($c))
861 {
862 if($en)
863 {
864 $c=sprintf('=%02X',$o);
865 $el=3;
866 $n=1;
867 $b=1;
868 }
869 else
870 $el=1;
871 if($break_lines
872 && $l+$el>75)
873 {
874 $e.='='.$this->line_break;
875 $l=0;
876 }
877 $e.=$c;
878 $l+=$el;
879 }
880 }
881 if(strlen($w))
882 {
883 if($break_lines
884 && $l+3>75)
885 $e.='='.$this->line_break;
886 $e.=sprintf('=%02X',Ord($w));
887 }
888 if($h
889 && $n)
890 return('=?'.$header_charset.'?q?'.$e.'?=');
891 else
892 return($e);
893 }
894
895 /*
896 {metadocument}
897 <function>
898 <name>WrapText</name>
899 <type>STRING</type>
900 <documentation>
901 <purpose>Split a text in lines that do not exceed the length limit
902 avoiding to break it in the middle of any words.</purpose>
903 <usage>Just pass the <argumentlink>
904 <function>WrapText</function>
905 <argument>text</argument>
906 </argumentlink> to be wrapped.</usage>
907 <returnvalue>The wrapped text eventually broken in multiple lines
908 that do not exceed the line length limit.</returnvalue>
909 </documentation>
910 <argument>
911 <name>text</name>
912 <type>STRING</type>
913 <documentation>
914 <purpose>Text to be wrapped.</purpose>
915 </documentation>
916 </argument>
917 <argument>
918 <name>line_length</name>
919 <type>INTEGER</type>
920 <defaultvalue>0</defaultvalue>
921 <documentation>
922 <purpose>Line length limit. Pass a value different than
923 <tt><integervalue>0</integervalue></tt> to use a line length
924 limit other than the default of 75 characters.</purpose>
925 </documentation>
926 </argument>
927 <argument>
928 <name>line_break</name>
929 <type>STRING</type>
930 <defaultvalue></defaultvalue>
931 <documentation>
932 <purpose>Character sequence that is used to break the lines longer
933 than the length limit. Pass a non-empty to use a line breaking
934 sequence other than the default
935 <tt><stringvalue>&#10;</stringvalue></tt>.</purpose>
936 </documentation>
937 </argument>
938 <argument>
939 <name>line_prefix</name>
940 <type>STRING</type>
941 <defaultvalue></defaultvalue>
942 <documentation>
943 <purpose>Character sequence that is used to insert in the beginning
944 of all lines.</purpose>
945 </documentation>
946 </argument>
947 <do>
948 {/metadocument}
949 */
950 Function WrapText($text,$line_length=0,$line_break="",$line_prefix="")
951 {
952 if(strlen($line_break)==0)
953 $line_break=$this->line_break;
954 if($line_length==0)
955 $line_length=$this->line_length;
956 $lines=explode("\n",str_replace("\r","\n",str_replace("\r\n","\n",$text)));
957 for($wrapped="",$line=0;$line<count($lines);++$line)
958 {
959 if(strlen($text_line=$lines[$line]))
960 {
961 for(;strlen($text_line=$line_prefix.$text_line)>$line_length;)
962 {
963 if(GetType($cut=strrpos(substr($text_line,0,$line_length)," "))!="integer"
964 || $cut<strlen($line_prefix))
965 {
966 if($this->break_long_lines)
967 {
968 $wrapped.=substr($text_line,0,$line_length).$line_break;
969 $cut=$line_length;
970 }
971 elseif(GetType($cut=strpos($text_line," ",$line_length))=="integer")
972 {
973 $wrapped.=substr($text_line, 0, $cut).$line_break;
974 ++$cut;
975 }
976 else
977 {
978 $wrapped.=$text_line.$line_break;
979 $cut=strlen($text_line);
980 }
981 }
982 else
983 {
984 $wrapped.=substr($text_line,0,$cut).$line_break;
985 ++$cut;
986 }
987 $text_line=substr($text_line,$cut);
988 }
989 }
990 $wrapped.=$text_line.$line_break;
991 }
992 return($wrapped);
993 }
994 /*
995 {metadocument}
996 </do>
997 </function>
998 {/metadocument}
999 */
1000
1001 /*
1002 {metadocument}
1003 <function>
1004 <name>CenterText</name>
1005 <type>STRING</type>
1006 <documentation>
1007 <purpose>Center a text in the middle of line.</purpose>
1008 <usage>Just pass the <argumentlink>
1009 <function>CenterText</function>
1010 <argument>text</argument>
1011 </argumentlink> to be centered.</usage>
1012 <returnvalue>The centered text.</returnvalue>
1013 </documentation>
1014 <argument>
1015 <name>text</name>
1016 <type>STRING</type>
1017 <documentation>
1018 <purpose>Text to be centered.</purpose>
1019 </documentation>
1020 </argument>
1021 <argument>
1022 <name>line_length</name>
1023 <type>INTEGER</type>
1024 <defaultvalue>0</defaultvalue>
1025 <documentation>
1026 <purpose>Line length limit. Pass a value different than
1027 <tt><integervalue>0</integervalue></tt> to use a line length
1028 limit other than the default of 75 characters.</purpose>
1029 </documentation>
1030 </argument>
1031 <do>
1032 {/metadocument}
1033 */
1034 Function CenterText($text, $line_length=0)
1035 {
1036 if($line_length==0)
1037 $line_length=$this->line_length;
1038 $length = strlen($text);
1039 if($length<$line_length)
1040 $text = str_repeat(' ', ($line_length-$length)/2).$text;
1041 return($text);
1042 }
1043 /*
1044 {metadocument}
1045 </do>
1046 </function>
1047 {/metadocument}
1048 */
1049
1050 /*
1051 {metadocument}
1052 <function>
1053 <name>Ruler</name>
1054 <type>STRING</type>
1055 <documentation>
1056 <purpose>Generate a line with characters that can be displayed as a
1057 separator ruler in a text message.</purpose>
1058 <returnvalue>The ruler line string.</returnvalue>
1059 </documentation>
1060 <argument>
1061 <name>line_length</name>
1062 <type>INTEGER</type>
1063 <defaultvalue>0</defaultvalue>
1064 <documentation>
1065 <purpose>Line length limit. Pass a value different than
1066 <tt><integervalue>0</integervalue></tt> to use a line length
1067 limit other than the default of 75 characters.</purpose>
1068 </documentation>
1069 </argument>
1070 <do>
1071 {/metadocument}
1072 */
1073 Function Ruler($line_length=0)
1074 {
1075 if($line_length==0)
1076 $line_length=$this->line_length;
1077 return(str_repeat($this->ruler, $line_length));
1078 }
1079 /*
1080 {metadocument}
1081 </do>
1082 </function>
1083 {/metadocument}
1084 */
1085
1086 /*
1087 {metadocument}
1088 <function>
1089 <name>QuoteText</name>
1090 <type>STRING</type>
1091 <documentation>
1092 <purpose>Mark a text block to appear like in reply messages composed
1093 with common e-mail programs that include text from the original
1094 message being replied.</purpose>
1095 <usage>Just pass the <argumentlink>
1096 <function>QuoteText</function>
1097 <argument>text</argument>
1098 </argumentlink> to be marked as a quote.</usage>
1099 <returnvalue>The quoted text with all lines prefixed with a quote
1100 prefix mark.</returnvalue>
1101 </documentation>
1102 <argument>
1103 <name>text</name>
1104 <type>STRING</type>
1105 <documentation>
1106 <purpose>Text to be quoted.</purpose>
1107 </documentation>
1108 </argument>
1109 <argument>
1110 <name>quote_prefix</name>
1111 <type>STRING</type>
1112 <defaultvalue></defaultvalue>
1113 <documentation>
1114 <purpose>Character sequence that is inserted in the beginning of
1115 all lines as a quote mark. Set to an empty string to tell the
1116 function to use the default specified by the
1117 <variablelink>line_quote_prefix</variablelink> variable.</purpose>
1118 </documentation>
1119 </argument>
1120 <do>
1121 {/metadocument}
1122 */
1123 Function QuoteText($text,$quote_prefix="")
1124 {
1125 if(strlen($quote_prefix)==0)
1126 $quote_prefix=$this->line_quote_prefix;
1127 return($this->WrapText($text,$line_length=0,$line_break="",$quote_prefix));
1128 }
1129 /*
1130 {metadocument}
1131 </do>
1132 </function>
1133 {/metadocument}
1134 */
1135
1136 /*
1137 {metadocument}
1138 <function>
1139 <name>SetHeader</name>
1140 <type>STRING</type>
1141 <documentation>
1142 <purpose>Set the value of a message header.</purpose>
1143 <usage>Use this function to set the values of the headers of the
1144 message that may be needed. There are some message headers that are
1145 automatically set by the class when the message is sent. Others
1146 must be defined before sending. Here follows the list of the names
1147 of the headers that must be set before sending:<paragraphbreak />
1148 <paragraphbreak />
1149 <b>Message subject</b> - <tt>Subject</tt><paragraphbreak />
1150 <b>Sender address</b> - <tt>From</tt><paragraphbreak />
1151 <b>Recipient addresses</b> - <tt>To</tt>, <tt>Cc</tt> and
1152 <tt>Bcc</tt><paragraphbreak />
1153 Each of the recipient address headers may contain one or more
1154 addresses. Multiple addresses must be separated by a comma and a
1155 space.<paragraphbreak />
1156 <b>Return path address</b> - <tt>Return-Path</tt><paragraphbreak />
1157 Optional header to specify the address where the message should be
1158 bounced in case it is not possible to deliver it.<paragraphbreak />
1159 In reality this is a virtual header. This means that adding this
1160 header to a message will not do anything by itself. However, this
1161 class looks for this header to adjust the message delivery
1162 procedure in such way that the Message Transfer Agent (MTA) system
1163 is hinted to direct any bounced messages to the address specified
1164 by this header.<paragraphbreak />
1165 Note that under some systems there is no way to set the return path
1166 address programmatically. This is the case when using the PHP
1167 <tt>mail()</tt> function under Windows where the return path
1168 address should be set in the <tt>php.ini</tt> configuration
1169 file.<paragraphbreak />
1170 Keep in mind that even when it is possible to set the return path
1171 address, the systems of some e-mail account providers may ignore
1172 this address and send bounced messages to the sender address. This
1173 is a bug of those systems. There is nothing that can be done other
1174 than complain.</usage>
1175 <returnvalue>An error message in case there was an error or an empty
1176 string otherwise. This return value may be safely ignored if the
1177 function parameters are set correctly.</returnvalue>
1178 </documentation>
1179 <argument>
1180 <name>header</name>
1181 <type>STRING</type>
1182 <documentation>
1183 <purpose>Name of the header.</purpose>
1184 </documentation>
1185 </argument>
1186 <argument>
1187 <name>value</name>
1188 <type>STRING</type>
1189 <documentation>
1190 <purpose>Text value for the header.</purpose>
1191 </documentation>
1192 </argument>
1193 <argument>
1194 <name>encoding_charset</name>
1195 <type>STRING</type>
1196 <defaultvalue></defaultvalue>
1197 <documentation>
1198 <purpose>Character set used in the header value. If it is set to an
1199 empty string, it is assumed the character set defined by the
1200 <variablelink>default_charset</variablelink> variable.</purpose>
1201 </documentation>
1202 </argument>
1203 <do>
1204 {/metadocument}
1205 */
1206 Function SetHeader($header, $value, $encoding_charset="")
1207 {
1208 if(strlen($this->error))
1209 return($this->error);
1210 $this->headers[strval($header)]=(!strcmp($encoding_charset,"") ? strval($value) : $this->QuotedPrintableEncode($value, $encoding_charset, 1, 0));
1211 return("");
1212 }
1213 /*
1214 {metadocument}
1215 </do>
1216 </function>
1217 {/metadocument}
1218 */
1219
1220 /*
1221 {metadocument}
1222 <function>
1223 <name>SetEncodedHeader</name>
1224 <type>STRING</type>
1225 <documentation>
1226 <purpose>The same as the <functionlink>SetHeader</functionlink>
1227 function assuming the default character set specified by the
1228 <variablelink>default_charset</variablelink> variable.</purpose>
1229 <usage>See the <functionlink>SetHeader</functionlink> function.</usage>
1230 <returnvalue>An error message in case there was an error or an empty
1231 string otherwise. This return value may be safely ignored if the
1232 function parameters are set correctly.</returnvalue>
1233 </documentation>
1234 <argument>
1235 <name>header</name>
1236 <type>STRING</type>
1237 <documentation>
1238 <purpose>Name of the header.</purpose>
1239 </documentation>
1240 </argument>
1241 <argument>
1242 <name>value</name>
1243 <type>STRING</type>
1244 <documentation>
1245 <purpose>Text value for the header.</purpose>
1246 </documentation>
1247 </argument>
1248 <argument>
1249 <name>encoding_charset</name>
1250 <type>STRING</type>
1251 <defaultvalue></defaultvalue>
1252 <documentation>
1253 <purpose>Character set used in the header value. If it is set to an
1254 empty string, it is assumed the character set defined by the
1255 <variablelink>default_charset</variablelink> variable.</purpose>
1256 </documentation>
1257 </argument>
1258 <do>
1259 {/metadocument}
1260 */
1261 Function SetEncodedHeader($header,$value, $encoding_charset = '')
1262 {
1263 return($this->SetHeader($header,$value,strlen($encoding_charset) ? $encoding_charset : $this->default_charset));
1264 }
1265 /*
1266 {metadocument}
1267 </do>
1268 </function>
1269 {/metadocument}
1270 */
1271
1272 /*
1273 {metadocument}
1274 <function>
1275 <name>SetEncodedEmailHeader</name>
1276 <type>STRING</type>
1277 <documentation>
1278 <purpose>Set the value of an header that is meant to represent the
1279 e-mail address of a person or entity with a known name. This is
1280 meant mostly to set the <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt> and
1281 <tt>Bcc</tt> headers.</purpose>
1282 <usage>Use this function like the
1283 <functionlink>SetHeader</functionlink> specifying the e-mail
1284 <argumentlink>
1285 <function>SetEncodedEmailHeader</function>
1286 <argument>address</argument>
1287 </argumentlink> as header value and also specifying the
1288 <argumentlink>
1289 <function>SetEncodedEmailHeader</function>
1290 <argument>name</argument>
1291 </argumentlink> of the known person or entity.</usage>
1292 <returnvalue>An error message in case there was an error or an empty
1293 string otherwise. This return value may be safely ignored if the
1294 function parameters are set correctly.</returnvalue>
1295 </documentation>
1296 <argument>
1297 <name>header</name>
1298 <type>STRING</type>
1299 <documentation>
1300 <purpose>Name of the header.</purpose>
1301 </documentation>
1302 </argument>
1303 <argument>
1304 <name>address</name>
1305 <type>STRING</type>
1306 <documentation>
1307 <purpose>E-mail address value.</purpose>
1308 </documentation>
1309 </argument>
1310 <argument>
1311 <name>name</name>
1312 <type>STRING</type>
1313 <documentation>
1314 <purpose>Person or entity name associated with the specified e-mail
1315 address.</purpose>
1316 </documentation>
1317 </argument>
1318 <argument>
1319 <name>encoding_charset</name>
1320 <type>STRING</type>
1321 <defaultvalue></defaultvalue>
1322 <documentation>
1323 <purpose>Character set used in the header value. If it is set to an
1324 empty string, it is assumed the character set defined by the
1325 <variablelink>default_charset</variablelink> variable.</purpose>
1326 </documentation>
1327 </argument>
1328 <do>
1329 {/metadocument}
1330 */
1331 Function SetEncodedEmailHeader($header, $address, $name, $encoding_charset = '')
1332 {
1333 return($this->SetHeader($header,$this->QuotedPrintableEncode($name, strlen($encoding_charset) ? $encoding_charset : $this->default_charset, 1, 1).' <'.$address.'>'));
1334 }
1335 /*
1336 {metadocument}
1337 </do>
1338 </function>
1339 {/metadocument}
1340 */
1341
1342 /*
1343 {metadocument}
1344 <function>
1345 <name>SetMultipleEncodedEmailHeader</name>
1346 <type>STRING</type>
1347 <documentation>
1348 <purpose>Set the value of an header that is meant to represent a list
1349 of e-mail addresses of names of people or entities. This is meant
1350 mostly to set the <tt>To</tt>, <tt>Cc</tt> and <tt>Bcc</tt>
1351 headers.</purpose>
1352 <usage>Use this function specifying the <argumentlink>
1353 <function>SetMultipleEncodedEmailHeader</function>
1354 <argument>header</argument>
1355 </argumentlink> and all the <argumentlink>
1356 <function>SetMultipleEncodedEmailHeader</function>
1357 <argument>addresses</argument>
1358 </argumentlink> in an associative array that should have
1359 the email addresses as entry indexes and the name of the respective
1360 people or entities as entry values.</usage>
1361 <returnvalue>An error message in case there was an error or an empty
1362 string otherwise. This return value may be safely ignored if the
1363 function parameters are set correctly.</returnvalue>
1364 <example><pre>$message_object->SetMultipleEncodedEmailHeader('Bcc', array(
1365 'peter@gabriel.org' =gt; 'Peter Gabriel',
1366 'paul@simon.net' =gt; 'Paul Simon',
1367 'mary@chain.com' =gt; 'Mary Chain'
1368 ));</pre></example>
1369 </documentation>
1370 <argument>
1371 <name>header</name>
1372 <type>STRING</type>
1373 <documentation>
1374 <purpose>Name of the header.</purpose>
1375 </documentation>
1376 </argument>
1377 <argument>
1378 <name>addresses</name>
1379 <type>HASH</type>
1380 <documentation>
1381 <purpose>List of all email addresses and associated person or
1382 entity names.</purpose>
1383 </documentation>
1384 </argument>
1385 <argument>
1386 <name>encoding_charset</name>
1387 <type>STRING</type>
1388 <defaultvalue></defaultvalue>
1389 <documentation>
1390 <purpose>Character set used in the header value. If it is set to an
1391 empty string, it is assumed the character set defined by the
1392 <variablelink>default_charset</variablelink> variable.</purpose>
1393 </documentation>
1394 </argument>
1395 <do>
1396 {/metadocument}
1397 */
1398 Function SetMultipleEncodedEmailHeader($header,$addresses, $encoding_charset = '')
1399 {
1400 Reset($addresses);
1401 $end=(GetType($address=Key($addresses))!="string");
1402 for($value="";!$end;)
1403 {
1404 if(strlen($value))
1405 $value.=", ";
1406 $value.=$this->QuotedPrintableEncode($addresses[$address], strlen($encoding_charset) ? $encoding_charset : $this->default_charset, 1, 1).' <'.$address.'>';
1407 Next($addresses);
1408 $end=(GetType($address=Key($addresses))!="string");
1409 }
1410 return($this->SetHeader($header,$value));
1411 }
1412 /*
1413 {metadocument}
1414 </do>
1415 </function>
1416 {/metadocument}
1417 */
1418
1419 /*
1420 {metadocument}
1421 <function>
1422 <name>ResetMessage</name>
1423 <type>VOID</type>
1424 <documentation>
1425 <purpose>Restore the content of the message to the initial state when
1426 the class object is created, i.e. without any headers or body
1427 parts.</purpose>
1428 <usage>Use this function if you want to start composing a completely
1429 new message.</usage>
1430 </documentation>
1431 <do>
1432 {/metadocument}
1433 */
1434 Function ResetMessage()
1435 {
1436 $this->headers=array();
1437 $this->body=-1;
1438 $this->body_parts=0;
1439 $this->parts=array();
1440 $this->total_parts=0;
1441 $this->free_parts=array();
1442 $this->total_free_parts=0;
1443 $this->delivery=array("State"=>"");
1444 $this->error="";
1445 }
1446 /*
1447 {metadocument}
1448 </do>
1449 </function>
1450 {/metadocument}
1451 */
1452
1453 Function CreatePart(&$definition,&$part)
1454 {
1455 $part=-1;
1456 if(strlen($this->error))
1457 return($this->error);
1458 if($this->total_free_parts)
1459 {
1460 $this->total_free_parts--;
1461 $part=$this->free_parts[$this->total_free_parts];
1462 Unset($this->free_parts[$this->total_free_parts]);
1463 }
1464 else
1465 {
1466 $part=$this->total_parts;
1467 ++$this->total_parts;
1468 }
1469 $this->parts[$part]=$definition;
1470 return("");
1471 }
1472
1473 /*
1474 {metadocument}
1475 <function>
1476 <name>AddPart</name>
1477 <type>STRING</type>
1478 <documentation>
1479 <purpose>Add a previously created part to the message.</purpose>
1480 <usage>Use any of the functions to create standalone message parts
1481 and then use this function to add them to the message.</usage>
1482 <returnvalue>An error message in case there was an error or an empty
1483 string otherwise. This return value may be safely ignored if the
1484 function parameters are set correctly.</returnvalue>
1485 </documentation>
1486 <argument>
1487 <name>part</name>
1488 <type>INTEGER</type>
1489 <documentation>
1490 <purpose>Number of the part as returned by the function that
1491 originally created it.</purpose>
1492 </documentation>
1493 </argument>
1494 <do>
1495 {/metadocument}
1496 */
1497 Function AddPart($part)
1498 {
1499 if(strlen($this->error))
1500 return($this->error);
1501 switch($this->body_parts)
1502 {
1503 case 0;
1504 $this->body=$part;
1505 break;
1506 case 1:
1507 $parts=array(
1508 $this->body,
1509 $part
1510 );
1511 if(strlen($error=$this->CreateMixedMultipart($parts,$body)))
1512 return($error);
1513 $this->body=$body;
1514 break;
1515 default:
1516 $this->parts[$this->body]["PARTS"][]=$part;
1517 break;
1518 }
1519 ++$this->body_parts;
1520 return("");
1521 }
1522 /*
1523 {metadocument}
1524 </do>
1525 </function>
1526 {/metadocument}
1527 */
1528
1529 /*
1530 {metadocument}
1531 <function>
1532 <name>ReplacePart</name>
1533 <type>STRING</type>
1534 <documentation>
1535 <purpose>Replace a message part already added to the message with a
1536 newly created part. The replaced part gets the definition of the
1537 replacing part. The replacing part is discarded and its part number
1538 becomes free for creation of a new part.</purpose>
1539 <usage>Use one of the functions to create message parts and then pass
1540 the returned part numbers to this function.</usage>
1541 <returnvalue>An error message in case there was an error or an empty
1542 string otherwise. This return value may be safely ignored if the
1543 function parameters are set correctly.</returnvalue>
1544 </documentation>
1545 <argument>
1546 <name>old_part</name>
1547 <type>INTEGER</type>
1548 <documentation>
1549 <purpose>Number of the previously added part.</purpose>
1550 </documentation>
1551 </argument>
1552 <argument>
1553 <name>new_part</name>
1554 <type>INTEGER</type>
1555 <documentation>
1556 <purpose>Number of the replacing part.</purpose>
1557 </documentation>
1558 </argument>
1559 <do>
1560 {/metadocument}
1561 */
1562 Function ReplacePart($old_part,$new_part)
1563 {
1564 if(strlen($this->error))
1565 return($this->error);
1566 if(!IsSet($this->parts[$old_part]))
1567 return($this->error="it was attempted to replace an invalid message part");
1568 if(IsSet($this->parts[$old_part]["FREE"]))
1569 return($this->error="it was attempted to replace a message part that is no longer valid");
1570 if(!IsSet($this->parts[$new_part]))
1571 return($this->error="it was attempted to use an invalid message replacecement part");
1572 if(IsSet($this->parts[$new_part]["FREE"]))
1573 return($this->error="it was attempted to use a message replacecement part that is no longer valid");
1574 $this->parts[$old_part]=$this->parts[$new_part];
1575 $this->parts[$new_part]=array("FREE"=>1);
1576 $this->free_parts[$this->total_free_parts]=$new_part;
1577 ++$this->total_free_parts;
1578 return("");
1579 }
1580 /*
1581 {metadocument}
1582 </do>
1583 </function>
1584 {/metadocument}
1585 */
1586
1587 Function CreateAndAddPart(&$definition)
1588 {
1589 if(strlen($error=$this->CreatePart($definition,$part))
1590 || strlen($error=$this->AddPart($part)))
1591 return($error);
1592 return("");
1593 }
1594
1595 /*
1596 {metadocument}
1597 <function>
1598 <name>CreatePlainTextPart</name>
1599 <type>STRING</type>
1600 <documentation>
1601 <purpose>Create a plain text message part.</purpose>
1602 <usage>Pass an ASCII (7 bits) <argumentlink>
1603 <function>CreatePlainTextPart</function>
1604 <argument>text</argument>
1605 </argumentlink> string and get the created part number in the
1606 <argumentlink>
1607 <function>CreatePlainTextPart</function>
1608 <argument>part</argument>
1609 </argumentlink> that is returned by reference.</usage>
1610 <returnvalue>An error message in case there was an error or an empty
1611 string otherwise. This return value may be safely ignored if the
1612 function parameters are set correctly.</returnvalue>
1613 </documentation>
1614 <argument>
1615 <name>text</name>
1616 <type>STRING</type>
1617 <documentation>
1618 <purpose>Text of the message part to create.</purpose>
1619 </documentation>
1620 </argument>
1621 <argument>
1622 <name>charset</name>
1623 <type>STRING</type>
1624 <documentation>
1625 <purpose>Character set used in the part text. If it is set to an
1626 empty string, it is assumed the character set defined by the
1627 <variablelink>default_charset</variablelink> variable.</purpose>
1628 </documentation>
1629 </argument>
1630 <argument>
1631 <name>part</name>
1632 <type>INTEGER</type>
1633 <out />
1634 <documentation>
1635 <purpose>Number of the created part that is returned by reference.</purpose>
1636 </documentation>
1637 </argument>
1638 <do>
1639 {/metadocument}
1640 */
1641 Function CreatePlainTextPart($text,$charset,&$part)
1642 {
1643 if(!strcmp($charset,""))
1644 $charset=$this->default_charset;
1645 $definition=array(
1646 "Content-Type"=>"text/plain",
1647 "DATA"=>$text
1648 );
1649 if(strcmp(strtoupper($charset),"ASCII"))
1650 $definition["CHARSET"]=$charset;
1651 return($this->CreatePart($definition,$part));
1652 }
1653 /*
1654 {metadocument}
1655 </do>
1656 </function>
1657 {/metadocument}
1658 */
1659
1660 /*
1661 {metadocument}
1662 <function>
1663 <name>AddPlainTextPart</name>
1664 <type>STRING</type>
1665 <documentation>
1666 <purpose>Add a plain text part to the message.</purpose>
1667 <usage>Pass an ASCII (7 bits) <argumentlink>
1668 <function>AddPlainTextPart</function>
1669 <argument>text</argument>
1670 </argumentlink> string.</usage>
1671 <returnvalue>An error message in case there was an error or an empty
1672 string otherwise. This return value may be safely ignored if the
1673 function parameters are set correctly.</returnvalue>
1674 </documentation>
1675 <argument>
1676 <name>text</name>
1677 <type>STRING</type>
1678 <documentation>
1679 <purpose>Text of the message part to add.</purpose>
1680 </documentation>
1681 </argument>
1682 <argument>
1683 <name>charset</name>
1684 <type>STRING</type>
1685 <defaultvalue></defaultvalue>
1686 <documentation>
1687 <purpose>Character set used in the part text. If it is set to an
1688 empty string, it is assumed the character set defined by the
1689 <variablelink>default_charset</variablelink> variable.</purpose>
1690 </documentation>
1691 </argument>
1692 <do>
1693 {/metadocument}
1694 */
1695 Function AddPlainTextPart($text,$charset="")
1696 {
1697 if(strlen($error=$this->CreatePlainTextPart($text,$charset,$part))
1698 || strlen($error=$this->AddPart($part)))
1699 return($error);
1700 return("");
1701 }
1702 /*
1703 {metadocument}
1704 </do>
1705 </function>
1706 {/metadocument}
1707 */
1708
1709 Function CreateEncodedQuotedPrintableTextPart($text,$charset,&$part)
1710 {
1711 if(!strcmp($charset,""))
1712 $charset=$this->default_charset;
1713 $definition=array(
1714 "Content-Type"=>"text/plain",
1715 "Content-Transfer-Encoding"=>"quoted-printable",
1716 "CHARSET"=>$charset,
1717 "DATA"=>$text
1718 );
1719 return($this->CreatePart($definition,$part));
1720 }
1721
1722 Function AddEncodedQuotedPrintableTextPart($text,$charset="")
1723 {
1724 if(strlen($error=$this->CreateEncodedQuotedPrintableTextPart($text,$charset,$part))
1725 || strlen($error=$this->AddPart($part)))
1726 return($error);
1727 return("");
1728 }
1729
1730 /*
1731 {metadocument}
1732 <function>
1733 <name>CreateQuotedPrintableTextPart</name>
1734 <type>STRING</type>
1735 <documentation>
1736 <purpose>Create a text message part that may contain non-ASCII
1737 characters (8 bits or more).</purpose>
1738 <usage>Pass a <argumentlink>
1739 <function>CreateQuotedPrintableTextPart</function>
1740 <argument>text</argument>
1741 </argumentlink> string and get the created part number in the
1742 <argumentlink>
1743 <function>CreateQuotedPrintableTextPart</function>
1744 <argument>part</argument>
1745 </argumentlink> that is returned by reference.</usage>
1746 <returnvalue>An error message in case there was an error or an empty
1747 string otherwise. This return value may be safely ignored if the
1748 function parameters are set correctly.</returnvalue>
1749 </documentation>
1750 <argument>
1751 <name>text</name>
1752 <type>STRING</type>
1753 <documentation>
1754 <purpose>Text of the message part to create.</purpose>
1755 </documentation>
1756 </argument>
1757 <argument>
1758 <name>charset</name>
1759 <type>STRING</type>
1760 <documentation>
1761 <purpose>Character set used in the part text. If it is set to an
1762 empty string, it is assumed the character set defined by the
1763 <variablelink>default_charset</variablelink> variable.</purpose>
1764 </documentation>
1765 </argument>
1766 <argument>
1767 <name>part</name>
1768 <type>INTEGER</type>
1769 <out />
1770 <documentation>
1771 <purpose>Number of the created part that is returned by reference.</purpose>
1772 </documentation>
1773 </argument>
1774 <do>
1775 {/metadocument}
1776 */
1777 Function CreateQuotedPrintableTextPart($text,$charset,&$part)
1778 {
1779 return($this->CreateEncodedQuotedPrintableTextPart($this->QuotedPrintableEncode($text),$charset,$part));
1780 }
1781 /*
1782 {metadocument}
1783 </do>
1784 </function>
1785 {/metadocument}
1786 */
1787
1788 /*
1789 {metadocument}
1790 <function>
1791 <name>AddQuotedPrintableTextPart</name>
1792 <type>STRING</type>
1793 <documentation>
1794 <purpose>Add a text part to the message that may contain non-ASCII
1795 characters (8 bits or more).</purpose>
1796 <usage>Pass a <argumentlink>
1797 <function>AddQuotedPrintableTextPart</function>
1798 <argument>text</argument>
1799 </argumentlink> string.</usage>
1800 <returnvalue>An error message in case there was an error or an empty
1801 string otherwise. This return value may be safely ignored if the
1802 function parameters are set correctly.</returnvalue>
1803 </documentation>
1804 <argument>
1805 <name>text</name>
1806 <type>STRING</type>
1807 <documentation>
1808 <purpose>Text of the message part to create.</purpose>
1809 </documentation>
1810 </argument>
1811 <argument>
1812 <name>charset</name>
1813 <type>STRING</type>
1814 <defaultvalue></defaultvalue>
1815 <documentation>
1816 <purpose>Character set used in the part text. If it is set to an
1817 empty string, it is assumed the character set defined by the
1818 <variablelink>default_charset</variablelink> variable.</purpose>
1819 </documentation>
1820 </argument>
1821 <do>
1822 {/metadocument}
1823 */
1824 Function AddQuotedPrintableTextPart($text,$charset="")
1825 {
1826 return($this->AddEncodedQuotedPrintableTextPart($this->QuotedPrintableEncode($text),$charset));
1827 }
1828 /*
1829 {metadocument}
1830 </do>
1831 </function>
1832 {/metadocument}
1833 */
1834
1835 /*
1836 {metadocument}
1837 <function>
1838 <name>CreateHTMLPart</name>
1839 <type>STRING</type>
1840 <documentation>
1841 <purpose>Create an HTML message part only with ASCII characters (7 bit).</purpose>
1842 <usage>Pass an ASCII (7 bits) <argumentlink>
1843 <function>CreateHTMLPart</function>
1844 <argument>html</argument>
1845 </argumentlink> text string and get the created part number in the
1846 <argumentlink>
1847 <function>CreateHTMLPart</function>
1848 <argument>part</argument>
1849 </argumentlink> that is returned by reference.</usage>
1850 <returnvalue>An error message in case there was an error or an empty
1851 string otherwise. This return value may be safely ignored if the
1852 function parameters are set correctly.</returnvalue>
1853 </documentation>
1854 <argument>
1855 <name>html</name>
1856 <type>STRING</type>
1857 <documentation>
1858 <purpose>HTML of the message part to create.</purpose>
1859 </documentation>
1860 </argument>
1861 <argument>
1862 <name>charset</name>
1863 <type>STRING</type>
1864 <documentation>
1865 <purpose>Character set used in the part text. If it is set to an
1866 empty string, it is assumed the character set defined by the
1867 <variablelink>default_charset</variablelink> variable.</purpose>
1868 </documentation>
1869 </argument>
1870 <argument>
1871 <name>part</name>
1872 <type>INTEGER</type>
1873 <out />
1874 <documentation>
1875 <purpose>Number of the created part that is returned by reference.</purpose>
1876 </documentation>
1877 </argument>
1878 <do>
1879 {/metadocument}
1880 */
1881 Function CreateHTMLPart($html,$charset,&$part)
1882 {
1883 if(!strcmp($charset,""))
1884 $charset=$this->default_charset;
1885 $definition=array(
1886 "Content-Type"=>"text/html",
1887 "CHARSET"=>$charset,
1888 "DATA"=>$html
1889 );
1890 return($this->CreatePart($definition,$part));
1891 }
1892 /*
1893 {metadocument}
1894 </do>
1895 </function>
1896 {/metadocument}
1897 */
1898
1899 /*
1900 {metadocument}
1901 <function>
1902 <name>AddHTMLPart</name>
1903 <type>STRING</type>
1904 <documentation>
1905 <purpose>Add an HTML part to the message only with ASCII characters.</purpose>
1906 <usage>Pass an <argumentlink>
1907 <function>AddHTMLPart</function>
1908 <argument>html</argument>
1909 </argumentlink> text string.</usage>
1910 <returnvalue>An error message in case there was an error or an empty
1911 string otherwise. This return value may be safely ignored if the
1912 function parameters are set correctly.</returnvalue>
1913 </documentation>
1914 <argument>
1915 <name>html</name>
1916 <type>STRING</type>
1917 <documentation>
1918 <purpose>HTML of the message part to create.</purpose>
1919 </documentation>
1920 </argument>
1921 <argument>
1922 <name>charset</name>
1923 <type>STRING</type>
1924 <defaultvalue></defaultvalue>
1925 <documentation>
1926 <purpose>Character set used in the part text. If it is set to an
1927 empty string, it is assumed the character set defined by the
1928 <variablelink>default_charset</variablelink> variable.</purpose>
1929 </documentation>
1930 </argument>
1931 <do>
1932 {/metadocument}
1933 */
1934 Function AddHTMLPart($html,$charset="")
1935 {
1936 if(strlen($error=$this->CreateHTMLPart($html,$charset,$part))
1937 || strlen($error=$this->AddPart($part)))
1938 return($error);
1939 return("");
1940 }
1941 /*
1942 {metadocument}
1943 </do>
1944 </function>
1945 {/metadocument}
1946 */
1947
1948 Function CreateEncodedQuotedPrintableHTMLPart($html,$charset,&$part)
1949 {
1950 if(!strcmp($charset,""))
1951 $charset=$this->default_charset;
1952 $definition=array(
1953 "Content-Type"=>"text/html",
1954 "Content-Transfer-Encoding"=>"quoted-printable",
1955 "CHARSET"=>$charset,
1956 "DATA"=>$html
1957 );
1958 return($this->CreatePart($definition,$part));
1959 }
1960
1961 Function AddEncodedQuotedPrintableHTMLPart($html,$charset="")
1962 {
1963 if(strlen($error=$this->CreateEncodedQuotedPrintableHTMLPart($html,$charset,$part))
1964 || strlen($error=$this->AddPart($part)))
1965 return($error);
1966 return("");
1967 }
1968
1969 /*
1970 {metadocument}
1971 <function>
1972 <name>CreateQuotedPrintableHTMLPart</name>
1973 <type>STRING</type>
1974 <documentation>
1975 <purpose>Create an HTML message part that may contain non-ASCII
1976 characters (8 bits or more).</purpose>
1977 <usage>Pass a <argumentlink>
1978 <function>CreateQuotedPrintableHTMLPart</function>
1979 <argument>html</argument>
1980 </argumentlink> text string and get the created part number in the
1981 <argumentlink>
1982 <function>CreateQuotedPrintableHTMLPart</function>
1983 <argument>part</argument>
1984 </argumentlink> that is returned by reference.</usage>
1985 <returnvalue>An error message in case there was an error or an empty
1986 string otherwise. This return value may be safely ignored if the
1987 function parameters are set correctly.</returnvalue>
1988 </documentation>
1989 <argument>
1990 <name>html</name>
1991 <type>STRING</type>
1992 <documentation>
1993 <purpose>HTML of the message part to create.</purpose>
1994 </documentation>
1995 </argument>
1996 <argument>
1997 <name>charset</name>
1998 <type>STRING</type>
1999 <documentation>
2000 <purpose>Character set used in the part text. If it is set to an
2001 empty string, it is assumed the character set defined by the
2002 <variablelink>default_charset</variablelink> variable.</purpose>
2003 </documentation>
2004 </argument>
2005 <argument>
2006 <name>part</name>
2007 <type>INTEGER</type>
2008 <out />
2009 <documentation>
2010 <purpose>Number of the created part that is returned by reference.</purpose>
2011 </documentation>
2012 </argument>
2013 <do>
2014 {/metadocument}
2015 */
2016 Function CreateQuotedPrintableHTMLPart($html,$charset,&$part)
2017 {
2018 return($this->CreateEncodedQuotedPrintableHTMLPart($this->QuotedPrintableEncode($html),$charset,$part));
2019 }
2020 /*
2021 {metadocument}
2022 </do>
2023 </function>
2024 {/metadocument}
2025 */
2026
2027
2028 /*
2029 {metadocument}
2030 <function>
2031 <name>AddQuotedPrintableHTMLPart</name>
2032 <type>STRING</type>
2033 <documentation>
2034 <purpose>Add an HTML part to the message that may contain non-ASCII
2035 characters (8 bits or more).</purpose>
2036 <usage>Pass a <argumentlink>
2037 <function>AddQuotedPrintableHTMLPart</function>
2038 <argument>html</argument>
2039 </argumentlink> text string.</usage>
2040 <returnvalue>An error message in case there was an error or an empty
2041 string otherwise. This return value may be safely ignored if the
2042 function parameters are set correctly.</returnvalue>
2043 </documentation>
2044 <argument>
2045 <name>html</name>
2046 <type>STRING</type>
2047 <documentation>
2048 <purpose>HTML of the message part to create.</purpose>
2049 </documentation>
2050 </argument>
2051 <argument>
2052 <name>charset</name>
2053 <type>STRING</type>
2054 <defaultvalue></defaultvalue>
2055 <documentation>
2056 <purpose>Character set used in the part text. If it is set to an
2057 empty string, it is assumed the character set defined by the
2058 <variablelink>default_charset</variablelink> variable.</purpose>
2059 </documentation>
2060 </argument>
2061 <do>
2062 {/metadocument}
2063 */
2064 Function AddQuotedPrintableHTMLPart($html,$charset="")
2065 {
2066 return($this->AddEncodedQuotedPrintableHTMLPart($this->QuotedPrintableEncode($html),$charset));
2067 }
2068 /*
2069 {metadocument}
2070 </do>
2071 </function>
2072 {/metadocument}
2073 */
2074
2075 Function GetFileDefinition($file, &$definition, $require_name=1)
2076 {
2077 if(strlen($this->error))
2078 return($this->error);
2079 $name="";
2080 if(IsSet($file["FileName"]))
2081 $name=basename($file["FileName"]);
2082 else
2083 {
2084 if(!IsSet($file["Data"]))
2085 return($this->OutputError("it was not specified the file part file name"));
2086 }
2087 if(IsSet($file["Name"]))
2088 $name=$file["Name"];
2089 if($require_name
2090 && strlen($name)==0)
2091 return($this->OutputError("it was not specified the file part name"));
2092 $encoding="base64";
2093 if(IsSet($file["Content-Type"]))
2094 {
2095 $content_type=$file["Content-Type"];
2096 $type=$this->Tokenize(strtolower($content_type),"/");
2097 $sub_type=$this->Tokenize("");
2098 switch($type)
2099 {
2100 case "text":
2101 case "image":
2102 case "audio":
2103 case "video":
2104 case "application":
2105 break;
2106 case "message":
2107 $encoding="7bit";
2108 break;
2109 case "automatic":
2110 switch($sub_type)
2111 {
2112 case "name":
2113 if(strlen($name)==0)
2114 return($this->OutputError("it is not possible to determine content type from the name"));
2115 switch(strtolower($this->GetFilenameExtension($name)))
2116 {
2117 case ".xls":
2118 $content_type="application/excel";
2119 break;
2120 case ".hqx":
2121 $content_type="application/macbinhex40";
2122 break;
2123 case ".doc":
2124 case ".dot":
2125 case ".wrd":
2126 $content_type="application/msword";
2127 break;
2128 case ".pdf":
2129 $content_type="application/pdf";
2130 break;
2131 case ".pgp":
2132 $content_type="application/pgp";
2133 break;
2134 case ".ps":
2135 case ".eps":
2136 case ".ai":
2137 $content_type="application/postscript";
2138 break;
2139 case ".ppt":
2140 $content_type="application/powerpoint";
2141 break;
2142 case ".rtf":
2143 $content_type="application/rtf";
2144 break;
2145 case ".tgz":
2146 case ".gtar":
2147 $content_type="application/x-gtar";
2148 break;
2149 case ".gz":
2150 $content_type="application/x-gzip";
2151 break;
2152 case ".php":
2153 case ".php3":
2154 $content_type="application/x-httpd-php";
2155 break;
2156 case ".js":
2157 $content_type="application/x-javascript";
2158 break;
2159 case ".ppd":
2160 case ".psd":
2161 $content_type="application/x-photoshop";
2162 break;
2163 case ".swf":
2164 case ".swc":
2165 case ".rf":
2166 $content_type="application/x-shockwave-flash";
2167 break;
2168 case ".tar":
2169 $content_type="application/x-tar";
2170 break;
2171 case ".zip":
2172 $content_type="application/zip";
2173 break;
2174 case ".mid":
2175 case ".midi":
2176 case ".kar":
2177 $content_type="audio/midi";
2178 break;
2179 case ".mp2":
2180 case ".mp3":
2181 case ".mpga":
2182 $content_type="audio/mpeg";
2183 break;
2184 case ".ra":
2185 $content_type="audio/x-realaudio";
2186 break;
2187 case ".wav":
2188 $content_type="audio/wav";
2189 break;
2190 case ".bmp":
2191 $content_type="image/bitmap";
2192 break;
2193 case ".gif":
2194 $content_type="image/gif";
2195 break;
2196 case ".iff":
2197 $content_type="image/iff";
2198 break;
2199 case ".jb2":
2200 $content_type="image/jb2";
2201 break;
2202 case ".jpg":
2203 case ".jpe":
2204 case ".jpeg":
2205 $content_type="image/jpeg";
2206 break;
2207 case ".jpx":
2208 $content_type="image/jpx";
2209 break;
2210 case ".png":
2211 $content_type="image/png";
2212 break;
2213 case ".tif":
2214 case ".tiff":
2215 $content_type="image/tiff";
2216 break;
2217 case ".wbmp":
2218 $content_type="image/vnd.wap.wbmp";
2219 break;
2220 case ".xbm":
2221 $content_type="image/xbm";
2222 break;
2223 case ".css":
2224 $content_type="text/css";
2225 break;
2226 case ".txt":
2227 $content_type="text/plain";
2228 break;
2229 case ".htm":
2230 case ".html":
2231 $content_type="text/html";
2232 break;
2233 case ".xml":
2234 $content_type="text/xml";
2235 break;
2236 case ".mpg":
2237 case ".mpe":
2238 case ".mpeg":
2239 $content_type="video/mpeg";
2240 break;
2241 case ".qt":
2242 case ".mov":
2243 $content_type="video/quicktime";
2244 break;
2245 case ".avi":
2246 $content_type="video/x-ms-video";
2247 break;
2248 case ".eml":
2249 $content_type="message/rfc822";
2250 $encoding="7bit";
2251 break;
2252 default:
2253 $content_type="application/octet-stream";
2254 break;
2255 }
2256 break;
2257 default:
2258 return($this->OutputError($content_type." is not a supported automatic content type detection method"));
2259 }
2260 break;
2261 default:
2262 return($this->OutputError($content_type." is not a supported file content type"));
2263 }
2264 }
2265 else
2266 $content_type="application/octet-stream";
2267 $definition=array(
2268 "Content-Type"=>$content_type,
2269 "Content-Transfer-Encoding"=>$encoding,
2270 "NAME"=>$name
2271 );
2272 if(IsSet($file["Disposition"]))
2273 {
2274 switch(strtolower($file["Disposition"]))
2275 {
2276 case "inline":
2277 case "attachment":
2278 break;
2279 default:
2280 return($this->OutputError($file["Disposition"]." is not a supported message part content disposition"));
2281 }
2282 $definition["DISPOSITION"]=$file["Disposition"];
2283 }
2284 if(IsSet($file["FileName"]))
2285 $definition["FILENAME"]=$file["FileName"];
2286 else
2287 {
2288 if(IsSet($file["Data"]))
2289 $definition["DATA"]=$file["Data"];
2290 }
2291 if(IsSet($file['Cache'])
2292 && $file['Cache'])
2293 $definition['Cache'] = 1;
2294 return("");
2295 }
2296
2297 /*
2298 {metadocument}
2299 <function>
2300 <name>CreateFilePart</name>
2301 <type>STRING</type>
2302 <documentation>
2303 <purpose>Create a message part to be handled as a file.</purpose>
2304 <usage>Pass a <argumentlink>
2305 <function>CreateFilePart</function>
2306 <argument>file</argument>
2307 </argumentlink> definition associative array and get the created
2308 part number in the <argumentlink>
2309 <function>CreateFilePart</function>
2310 <argument>part</argument>
2311 </argumentlink> that is returned by reference.</usage>
2312 <returnvalue>An error message in case there was an error or an empty
2313 string otherwise. This return value may be safely ignored if the
2314 function parameters are set correctly.</returnvalue>
2315 </documentation>
2316 <argument>
2317 <name>file</name>
2318 <type>HASH</type>
2319 <documentation>
2320 <purpose>Associative array to specify parameters that describe the
2321 file part. Here follows the list of supported parameters that
2322 should be used as indexes of the array:<paragraphbreak />
2323 <tt>FileName</tt><paragraphbreak />
2324 Name of the file from which the part data will be read when the
2325 message is generated. It may be a remote URL as long as your PHP
2326 installation is configured to allow accessing remote files with
2327 the <tt>fopen()</tt> function.<paragraphbreak />
2328 <tt>Data</tt><paragraphbreak />
2329 String that specifies the data of the file. This should be used
2330 as alternative data source to <tt>FileName</tt> for passing data
2331 available in memory, like for instance files stored in a database
2332 that was queried dynamically and the file contents was fetched
2333 into a string variable.<paragraphbreak />
2334 <tt>Name</tt><paragraphbreak />
2335 Name of the file that will appear in the message. If this
2336 parameter is missing the base name of the <tt>FileName</tt>
2337 parameter is used, if present.<paragraphbreak />
2338 <tt>Content-Type</tt><paragraphbreak />
2339 Content type of the part: <tt>text/plain</tt> for text,
2340 <tt>text/html</tt> for HTML, <tt>image/gif</tt> for GIF images,
2341 etc..<paragraphbreak />
2342 There is one special type named <tt>automatic/name</tt> that may
2343 be used to tell the class to try to guess the content type from
2344 the file name. Many file types are recognized from the file name
2345 extension. If the file name extension is not recognized, the
2346 default for binary data <tt>application/octet-stream</tt> is
2347 assumed.<paragraphbreak />
2348 <tt>Disposition</tt><paragraphbreak />
2349 Information to whether this file part is meant to be used as a
2350 file <tt>attachment</tt> or as a part meant to be displayed
2351 <tt>inline</tt>, eventually integrated with another related
2352 part.<paragraphbreak />
2353 <tt>Cache</tt><paragraphbreak />
2354 Boolean flag that indicates that this message part should be
2355 cached when generating the message body. Use only when sending
2356 many messages to multiple recipients, but this part does not
2357 change between each of the messages that are sent.<paragraphbreak />
2358 Note that it is also not worth using this option when setting the
2359 <variablelink>cache_body</variablelink>, as that variable makes
2360 the class cache the whole message body and the internal message
2361 parts will not be rebuilt.</purpose>
2362 </documentation>
2363 </argument>
2364 <argument>
2365 <name>part</name>
2366 <type>INTEGER</type>
2367 <out />
2368 <documentation>
2369 <purpose>Number of the created part that is returned by reference.</purpose>
2370 </documentation>
2371 </argument>
2372 <do>
2373 {/metadocument}
2374 */
2375 Function CreateFilePart(&$file,&$part)
2376 {
2377 if(strlen($this->GetFileDefinition($file,$definition)))
2378 return($this->error);
2379 return($this->CreatePart($definition,$part));
2380 }
2381 /*
2382 {metadocument}
2383 </do>
2384 </function>
2385 {/metadocument}
2386 */
2387
2388 /*
2389 {metadocument}
2390 <function>
2391 <name>AddFilePart</name>
2392 <type>STRING</type>
2393 <documentation>
2394 <purpose>Add a message part to be handled as a file.</purpose>
2395 <usage>Pass a <argumentlink>
2396 <function>AddFilePart</function>
2397 <argument>file</argument>
2398 </argumentlink> definition associative array.</usage>
2399 <returnvalue>An error message in case there was an error or an empty
2400 string otherwise. This return value may be safely ignored if the
2401 function parameters are set correctly.</returnvalue>
2402 </documentation>
2403 <argument>
2404 <name>file</name>
2405 <type>HASH</type>
2406 <documentation>
2407 <purpose>Associative array to specify parameters that describe the
2408 file part. See the <argumentlink>
2409 <function>CreateFilePart</function>
2410 <argument>file</argument>
2411 </argumentlink> argument description of the
2412 <functionlink>CreateFilePart</functionlink> function for an
2413 explanation about the supported file parameters.</purpose>
2414 </documentation>
2415 </argument>
2416 <do>
2417 {/metadocument}
2418 */
2419 Function AddFilePart(&$file)
2420 {
2421 if(strlen($error=$this->CreateFilePart($file,$part))
2422 || strlen($error=$this->AddPart($part)))
2423 return($error);
2424 return("");
2425 }
2426 /*
2427 {metadocument}
2428 </do>
2429 </function>
2430 {/metadocument}
2431 */
2432
2433 /*
2434 {metadocument}
2435 <function>
2436 <name>CreateMessagePart</name>
2437 <type>STRING</type>
2438 <documentation>
2439 <purpose>Create a message part to encapsulate another message. This
2440 is usually meant to create an attachment that contains a message
2441 that was received and is being forwarded intact with the original
2442 the headers and body data.</purpose>
2443 <usage>This function should be used like the
2444 <functionlink>CreateFilePart</functionlink> function, passing the
2445 same parameters to the <argumentlink>
2446 <function>CreateMessagePart</function>
2447 <argument>message</argument>
2448 </argumentlink> argument.<paragraphbreak />
2449 The message to be encapsulated can be specified either as an
2450 existing file with the <tt>FileName</tt> parameter, or as string
2451 of data in memory with the <tt>Data</tt>
2452 parameter.<paragraphbreak />
2453 The <tt>Content-Type</tt> and <tt>Disposition</tt> file parameters
2454 do not need to be specified because they are overridden by this
2455 function.</usage>
2456 <returnvalue>An error message in case there was an error or an empty
2457 string otherwise. This return value may be safely ignored if the
2458 function parameters are set correctly.</returnvalue>
2459 </documentation>
2460 <argument>
2461 <name>message</name>
2462 <type>HASH</type>
2463 <documentation>
2464 <purpose>Associative array that specifies definition parameters of
2465 the message file part.</purpose>
2466 </documentation>
2467 </argument>
2468 <argument>
2469 <name>part</name>
2470 <type>INTEGER</type>
2471 <out />
2472 <documentation>
2473 <purpose>Number of the created part that is returned by reference.</purpose>
2474 </documentation>
2475 </argument>
2476 <do>
2477 {/metadocument}
2478 */
2479 Function CreateMessagePart(&$message,&$part)
2480 {
2481 $message["Content-Type"]="message/rfc822";
2482 $message["Disposition"]="inline";
2483 if(strlen($this->GetFileDefinition($message,$definition)))
2484 return($this->error);
2485 return($this->CreatePart($definition,$part));
2486 }
2487 /*
2488 {metadocument}
2489 </do>
2490 </function>
2491 {/metadocument}
2492 */
2493
2494 /*
2495 {metadocument}
2496 <function>
2497 <name>AddMessagePart</name>
2498 <type>STRING</type>
2499 <documentation>
2500 <purpose>Add a message part that encapsulates another message. This
2501 is usually meant to add an attachment that contains a message that
2502 was received and is being forwarded intact with the original the
2503 headers and body data.</purpose>
2504 <usage>This function should be used like the
2505 <functionlink>AddFilePart</functionlink> function, passing the
2506 same parameters to the <argumentlink>
2507 <function>AddMessagePart</function>
2508 <argument>message</argument>
2509 </argumentlink> argument. See the
2510 <functionlink>CreateFilePart</functionlink> function for more
2511 details.</usage>
2512 <returnvalue>An error message in case there was an error or an empty
2513 string otherwise. This return value may be safely ignored if the
2514 function parameters are set correctly.</returnvalue>
2515 </documentation>
2516 <argument>
2517 <name>message</name>
2518 <type>HASH</type>
2519 <documentation>
2520 <purpose>Associative array that specifies definition parameters of
2521 the message file part.</purpose>
2522 </documentation>
2523 </argument>
2524 <do>
2525 {/metadocument}
2526 */
2527 Function AddMessagePart(&$message)
2528 {
2529 if(strlen($error=$this->CreateMessagePart($message,$part))
2530 || strlen($error=$this->AddPart($part)))
2531 return($error);
2532 return("");
2533 }
2534 /*
2535 {metadocument}
2536 </do>
2537 </function>
2538 {/metadocument}
2539 */
2540
2541 Function CreateMultipart(&$parts,&$part,$type)
2542 {
2543 $definition=array(
2544 "Content-Type"=>"multipart/".$type,
2545 "PARTS"=>$parts
2546 );
2547 return($this->CreatePart($definition,$part));
2548 }
2549
2550 Function AddMultipart(&$parts,$type)
2551 {
2552 if(strlen($error=$this->CreateMultipart($parts,$part,$type))
2553 || strlen($error=$this->AddPart($part)))
2554 return($error);
2555 return("");
2556 }
2557
2558 /*
2559 {metadocument}
2560 <function>
2561 <name>CreateAlternativeMultipart</name>
2562 <type>STRING</type>
2563 <documentation>
2564 <purpose>Create a message part composed of multiple parts that can be
2565 displayed by the recipient e-mail program in alternative
2566 formats.<paragraphbreak />
2567 This is usually meant to create HTML messages with an alternative
2568 text part to be displayed by programs that cannot display HTML
2569 messages.</purpose>
2570 <usage>Create all the alternative message parts that are going to be
2571 sent and pass their numbers to the <argumentlink>
2572 <function>CreateAlternativeMultipart</function>
2573 <argument>parts</argument>
2574 </argumentlink> array argument.<paragraphbreak />
2575 The least sophisticated part, usually the text part, should appear
2576 first in the parts array because the e-mail programs that support
2577 displaying more sophisticated message parts will pick the last part
2578 in the message that is supported.</usage>
2579 <returnvalue>An error message in case there was an error or an empty
2580 string otherwise. This return value may be safely ignored if the
2581 function parameters are set correctly.</returnvalue>
2582 </documentation>
2583 <argument>
2584 <name>parts</name>
2585 <type>ARRAY</type>
2586 <documentation>
2587 <purpose>Array with the numbers with all the alternative parts.</purpose>
2588 </documentation>
2589 </argument>
2590 <argument>
2591 <name>part</name>
2592 <type>INTEGER</type>
2593 <out />
2594 <documentation>
2595 <purpose>Number of the created part that is returned by reference.</purpose>
2596 </documentation>
2597 </argument>
2598 <do>
2599 {/metadocument}
2600 */
2601 Function CreateAlternativeMultipart(&$parts,&$part)
2602 {
2603 return($this->CreateMultiPart($parts,$part,"alternative"));
2604 }
2605 /*
2606 {metadocument}
2607 </do>
2608 </function>
2609 {/metadocument}
2610 */
2611
2612 /*
2613 {metadocument}
2614 <function>
2615 <name>AddAlternativeMultipart</name>
2616 <type>STRING</type>
2617 <documentation>
2618 <purpose>Add a message part composed of multiple parts that can be
2619 displayed by the recipient e-mail program in alternative
2620 formats.<paragraphbreak />
2621 This is usually meant to create HTML messages with an alternative
2622 text part to be displayed by programs that cannot display HTML
2623 messages.</purpose>
2624 <usage>Create all the alternative message parts that are going to be
2625 sent and pass their numbers to the <argumentlink>
2626 <function>AddAlternativeMultipart</function>
2627 <argument>parts</argument>
2628 </argumentlink> array argument.<paragraphbreak />
2629 The least sophisticated part, usually the text part, should appear
2630 first in the parts array because the e-mail programs that support
2631 displaying more sophisticated message parts will pick the last part
2632 in the message that is supported.</usage>
2633 <returnvalue>An error message in case there was an error or an empty
2634 string otherwise. This return value may be safely ignored if the
2635 function parameters are set correctly.</returnvalue>
2636 </documentation>
2637 <argument>
2638 <name>parts</name>
2639 <type>ARRAY</type>
2640 <documentation>
2641 <purpose>Array with the numbers with all the alternative parts.</purpose>
2642 </documentation>
2643 </argument>
2644 <do>
2645 {/metadocument}
2646 */
2647 Function AddAlternativeMultipart(&$parts)
2648 {
2649 return($this->AddMultipart($parts,"alternative"));
2650 }
2651 /*
2652 {metadocument}
2653 </do>
2654 </function>
2655 {/metadocument}
2656 */
2657
2658 /*
2659 {metadocument}
2660 <function>
2661 <name>CreateRelatedMultipart</name>
2662 <type>STRING</type>
2663 <documentation>
2664 <purpose>Create a message part that groups several related
2665 parts.<paragraphbreak />
2666 This is usually meant to group an HTML message part with images or
2667 other types of files that should be embedded in the same message
2668 and be displayed as a single part by the recipient e-mail
2669 program.</purpose>
2670 <usage>Create all the related message parts that are going to be
2671 sent and pass their numbers to the <argumentlink>
2672 <function>CreateRelatedMultipart</function>
2673 <argument>parts</argument>
2674 </argumentlink> array argument.<paragraphbreak />
2675 When using this function to group an HTML message with embedded
2676 images or other related files, make sure that the HTML part number
2677 is the first listed in the <argumentlink>
2678 <function>CreateRelatedMultipart</function>
2679 <argument>parts</argument>
2680 </argumentlink> array argument, or else the message may not appear
2681 correctly.</usage>
2682 <returnvalue>An error message in case there was an error or an empty
2683 string otherwise. This return value may be safely ignored if the
2684 function parameters are set correctly.</returnvalue>
2685 </documentation>
2686 <argument>
2687 <name>parts</name>
2688 <type>ARRAY</type>
2689 <documentation>
2690 <purpose>Array with the numbers with all the related parts.</purpose>
2691 </documentation>
2692 </argument>
2693 <argument>
2694 <name>part</name>
2695 <type>INTEGER</type>
2696 <out />
2697 <documentation>
2698 <purpose>Number of the created part that is returned by reference.</purpose>
2699 </documentation>
2700 </argument>
2701 <do>
2702 {/metadocument}
2703 */
2704 Function CreateRelatedMultipart(&$parts,&$part)
2705 {
2706 return($this->CreateMultipart($parts,$part,"related"));
2707 }
2708 /*
2709 {metadocument}
2710 </do>
2711 </function>
2712 {/metadocument}
2713 */
2714
2715 /*
2716 {metadocument}
2717 <function>
2718 <name>AddRelatedMultipart</name>
2719 <type>STRING</type>
2720 <documentation>
2721 <purpose>Add a message part that groups several related
2722 parts.<paragraphbreak />
2723 This is usually meant to group an HTML message part with images or
2724 other types of files that should be embedded in the same message
2725 and be displayed as a single part by the recipient e-mail
2726 program.</purpose>
2727 <usage>Create all the related message parts that are going to be
2728 sent and pass their numbers to the <argumentlink>
2729 <function>AddRelatedMultipart</function>
2730 <argument>parts</argument>
2731 </argumentlink> array argument.<paragraphbreak />
2732 When using this function to group an HTML message with embedded
2733 images or other related files, make sure that the HTML part number
2734 is the first listed in the <argumentlink>
2735 <function>AddRelatedMultipart</function>
2736 <argument>parts</argument>
2737 </argumentlink> array argument, or else the message may not appear
2738 correctly.</usage>
2739 <returnvalue>An error message in case there was an error or an empty
2740 string otherwise. This return value may be safely ignored if the
2741 function parameters are set correctly.</returnvalue>
2742 </documentation>
2743 <argument>
2744 <name>parts</name>
2745 <type>ARRAY</type>
2746 <documentation>
2747 <purpose>Array with the numbers with all the related parts.</purpose>
2748 </documentation>
2749 </argument>
2750 <do>
2751 {/metadocument}
2752 */
2753 Function AddRelatedMultipart(&$parts)
2754 {
2755 return($this->AddMultipart($parts,"related"));
2756 }
2757 /*
2758 {metadocument}
2759 </do>
2760 </function>
2761 {/metadocument}
2762 */
2763
2764 /*
2765 {metadocument}
2766 <function>
2767 <name>CreateMixedMultipart</name>
2768 <type>STRING</type>
2769 <documentation>
2770 <purpose>Create a message part that groups several independent
2771 parts.<paragraphbreak />
2772 Usually this is meant compose messages with one or more file
2773 attachments. However, it is not necessary to use this function as
2774 the class implicitly creates a <tt>multipart/mixed</tt> message
2775 when more than one part is added to the message.</purpose>
2776 <usage>Create all the independent message parts that are going to be
2777 sent and pass their numbers to the <argumentlink>
2778 <function>CreateMixedMultipart</function>
2779 <argument>parts</argument>
2780 </argumentlink> array argument.</usage>
2781 <returnvalue>An error message in case there was an error or an empty
2782 string otherwise. This return value may be safely ignored if the
2783 function parameters are set correctly.</returnvalue>
2784 </documentation>
2785 <argument>
2786 <name>parts</name>
2787 <type>ARRAY</type>
2788 <documentation>
2789 <purpose>Array with the numbers with all the related parts.</purpose>
2790 </documentation>
2791 </argument>
2792 <argument>
2793 <name>part</name>
2794 <type>INTEGER</type>
2795 <out />
2796 <documentation>
2797 <purpose>Number of the created part that is returned by reference.</purpose>
2798 </documentation>
2799 </argument>
2800 <do>
2801 {/metadocument}
2802 */
2803 Function CreateMixedMultipart(&$parts,&$part)
2804 {
2805 return($this->CreateMultipart($parts,$part,"mixed"));
2806 }
2807 /*
2808 {metadocument}
2809 </do>
2810 </function>
2811 {/metadocument}
2812 */
2813
2814 /*
2815 {metadocument}
2816 <function>
2817 <name>AddMixedMultipart</name>
2818 <type>STRING</type>
2819 <documentation>
2820 <purpose>Add a message part that groups several independent
2821 parts.<paragraphbreak />
2822 Usually this is meant compose messages with one or more file
2823 attachments. However, it is not necessary to use this function as
2824 the class implicitly creates a <tt>multipart/mixed</tt> message
2825 when more than one part is added to the message.</purpose>
2826 <usage>Create all the independent message parts that are going to be
2827 sent and pass their numbers to the <argumentlink>
2828 <function>AddMixedMultipart</function>
2829 <argument>parts</argument>
2830 </argumentlink> array argument.</usage>
2831 <returnvalue>An error message in case there was an error or an empty
2832 string otherwise. This return value may be safely ignored if the
2833 function parameters are set correctly.</returnvalue>
2834 </documentation>
2835 <argument>
2836 <name>parts</name>
2837 <type>ARRAY</type>
2838 <documentation>
2839 <purpose>Array with the numbers with all the related parts.</purpose>
2840 </documentation>
2841 </argument>
2842 <do>
2843 {/metadocument}
2844 */
2845 Function AddMixedMultipart(&$parts)
2846 {
2847 return($this->AddMultipart($parts,"mixed"));
2848 }
2849 /*
2850 {metadocument}
2851 </do>
2852 </function>
2853 {/metadocument}
2854 */
2855
2856 Function CreateParallelMultipart(&$parts,&$part)
2857 {
2858 return($this->CreateMultipart($parts,$part,"paralell"));
2859 }
2860
2861 Function AddParalellMultipart(&$parts)
2862 {
2863 return($this->AddMultipart($parts,"paralell"));
2864 }
2865
2866 /*
2867 {metadocument}
2868 <function>
2869 <name>GetPartContentID</name>
2870 <type>STRING</type>
2871 <documentation>
2872 <purpose>Retrieve the content identifier associated to a given
2873 message part.</purpose>
2874 <usage>Create a message part and pass its number to the <argumentlink>
2875 <function>GetPartContentID</function>
2876 <argument>part</argument>
2877 </argumentlink> argument.<paragraphbreak />
2878 This function is usually meant to create an URL that can be used
2879 in an HTML message part to reference related parts like images, CSS
2880 (Cascaded Style Sheets), or any other type of files related to the
2881 HTML part that are embedded in the same message as part of a
2882 <tt>multipart/related</tt> composite part.<paragraphbreak />
2883 To use the part content identifier returned by this function you
2884 need to prepend the string <tt><stringvalue>cid:</stringvalue></tt>
2885 to form a special URL that can be used in the HTML document this
2886 part file.<paragraphbreak />
2887 You may read more about using this function in the class usage
2888 section about <link>
2889 <data>embedding images in HTML messages</data>
2890 <name>embed-image</name>
2891 </link>.</usage>
2892 <returnvalue>The content identifier text string.<paragraphbreak />
2893 If it is specified an invalid message part, this function returns
2894 an empty string.</returnvalue>
2895 </documentation>
2896 <argument>
2897 <name>part</name>
2898 <type>INTEGER</type>
2899 <documentation>
2900 <purpose>Number of the part as returned by the function that
2901 originally created it.</purpose>
2902 </documentation>
2903 </argument>
2904 <do>
2905 {/metadocument}
2906 */
2907 Function GetPartContentID($part)
2908 {
2909 if(!IsSet($this->parts[$part]))
2910 return("");
2911 if(!IsSet($this->parts[$part]["Content-ID"]))
2912 {
2913 $extension=(IsSet($this->parts[$part]["NAME"]) ? $this->GetFilenameExtension($this->parts[$part]["NAME"]) : "");
2914 $this->parts[$part]["Content-ID"]=md5(uniqid($part.time())).$extension;
2915 }
2916 return($this->parts[$part]["Content-ID"]);
2917 }
2918 /*
2919 {metadocument}
2920 </do>
2921 </function>
2922 {/metadocument}
2923 */
2924
2925 /*
2926 {metadocument}
2927 <function>
2928 <name>GetDataURL</name>
2929 <type>STRING</type>
2930 <documentation>
2931 <purpose>Generate a <tt>data:</tt> URL according to the <link>
2932 <data>RFC 2397</data>
2933 <url>http://www.ietf.org/rfc/rfc2397.txt</url>
2934 </link> suitable for using in HTML messages to represent an image
2935 or other type of file on which the data is directly embedded in the
2936 HTML code instead of being fetched from a separate file or remote
2937 URL.<paragraphbreak />
2938 Note that not all e-mail programs are capable of displaying images
2939 or other types of files embedded in HTML messages this way.</purpose>
2940 <usage>Pass a <argumentlink>
2941 <function>GetDataURL</function>
2942 <argument>file</argument>
2943 </argumentlink> part definition array like for the
2944 <functionlink>CreateFilePart</functionlink> function.</usage>
2945 <returnvalue>The <tt>data:</tt> representing the described file or an
2946 empty string in case there was an error.</returnvalue>
2947 </documentation>
2948 <argument>
2949 <name>file</name>
2950 <type>HASH</type>
2951 <documentation>
2952 <purpose>File definition.</purpose>
2953 </documentation>
2954 </argument>
2955 <do>
2956 {/metadocument}
2957 */
2958 Function GetDataURL($file)
2959 {
2960 if(strlen($this->GetFileDefinition($file,$definition,0)))
2961 return($this->error);
2962 if(IsSet($definition["FILENAME"]))
2963 {
2964 $size=@filesize($definition["FILENAME"]);
2965 if(!($file=@fopen($definition["FILENAME"],"rb")))
2966 return($this->OutputPHPError("could not open data file ".$definition["FILENAME"], $php_errormsg));
2967 for($body="";!feof($file);)
2968 {
2969 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
2970 {
2971 $this->OutputPHPError("could not read data file", $php_errormsg);
2972 fclose($file);
2973 return("");
2974 }
2975 $body.=$block;
2976 }
2977 fclose($file);
2978 if(GetType($size)=="integer"
2979 && strlen($body)!=$size)
2980 {
2981 $this->OutputError("the length of the file that was read does not match the size of the part file ".$definition["FILENAME"]." due to possible data corruption");
2982 return("");
2983 }
2984 if(function_exists("ini_get")
2985 && ini_get("magic_quotes_runtime"))
2986 $body=StripSlashes($body);
2987 $body=chunk_split(base64_encode($body));
2988 }
2989 else
2990 {
2991 if(!IsSet($definition["DATA"]))
2992 {
2993 $this->OutputError("it was not specified a file or data block");
2994 return("");
2995 }
2996 $body=chunk_split(base64_encode($definition["DATA"]));
2997 }
2998 return("data:".$definition["Content-Type"].";base64,".$body);
2999 }
3000 /*
3001 {metadocument}
3002 </do>
3003 </function>
3004 {/metadocument}
3005 */
3006
3007 Function GetHeadersAndBody(&$headers, &$body)
3008 {
3009 $headers=$this->headers;
3010 if(strcmp($this->mailer,""))
3011 {
3012 $headers["X-Mailer"]=$this->mailer;
3013 if(strlen($this->mailer_delivery))
3014 $headers["X-Mailer"].=' ('.$this->mailer_delivery.')';
3015 }
3016 $headers["MIME-Version"]="1.0";
3017 if($this->body_parts==0)
3018 return($this->OutputError("message has no body parts"));
3019 if(strlen($error=$this->GetPartHeaders($headers,$this->body)))
3020 return($error);
3021 if($this->cache_body
3022 && IsSet($this->body_cache[$this->body]))
3023 $body=$this->body_cache[$this->body];
3024 else
3025 {
3026 if(strlen($error=$this->GetPartBody($body,$this->body)))
3027 return($error);
3028 if($this->cache_body)
3029 $this->body_cache[$this->body]=$body;
3030 }
3031 return("");
3032 }
3033
3034 /*
3035 {metadocument}
3036 <function>
3037 <name>Send</name>
3038 <type>STRING</type>
3039 <documentation>
3040 <purpose>Send a composed message.</purpose>
3041 <usage>Use this function after you have set the necessary message
3042 headers and added the message body parts.</usage>
3043 <returnvalue>An error message in case there was an error or an empty
3044 string otherwise. This return value may be safely ignored if the
3045 function parameters are set correctly.</returnvalue>
3046 </documentation>
3047 <do>
3048 {/metadocument}
3049 */
3050 Function Send()
3051 {
3052 if(strlen($this->error))
3053 return($this->error);
3054 if(strlen($error=$this->GetHeadersAndBody($headers, $body)))
3055 return($error);
3056 if(strcmp($error=$this->StartSendingMessage(),""))
3057 return($error);
3058 if(strlen($error=$this->SendMessageHeaders($headers))==0
3059 && strlen($error=$this->SendMessageBody($body))==0)
3060 $error=$this->EndSendingMessage();
3061 $this->StopSendingMessage();
3062 return($error);
3063 }
3064 /*
3065 {metadocument}
3066 </do>
3067 </function>
3068 {/metadocument}
3069 */
3070
3071 /*
3072 {metadocument}
3073 <function>
3074 <name>GetMessage</name>
3075 <type>STRING</type>
3076 <documentation>
3077 <purpose>Get the whole message headers and body.</purpose>
3078 <usage>Use this function to retrieve the message headers and body
3079 without sending it.</usage>
3080 <returnvalue>An error message in case there was an error or an empty
3081 string otherwise. This return value may be safely ignored if the
3082 function parameters are set correctly.</returnvalue>
3083 </documentation>
3084 <argument>
3085 <name>message</name>
3086 <type>STRING</type>
3087 <out />
3088 <documentation>
3089 <purpose>Reference to a string variable to store the text of the
3090 message headers and body.</purpose>
3091 </documentation>
3092 </argument>
3093 <do>
3094 {/metadocument}
3095 */
3096 Function GetMessage(&$message)
3097 {
3098 if(strlen($this->error))
3099 return($this->error);
3100 if(strlen($error=$this->GetHeadersAndBody($headers, $body)))
3101 return($error);
3102 for($message="", $h=0, Reset($headers); $h<count($headers); ++$h, Next($headers))
3103 {
3104 $name=Key($headers);
3105 $message.=$name.": ".$headers[$name].$this->line_break;
3106 }
3107 $message.=$this->line_break;
3108 $message.=$body;
3109 return("");
3110 }
3111 /*
3112 {metadocument}
3113 </do>
3114 </function>
3115 {/metadocument}
3116 */
3117
3118 /*
3119 {metadocument}
3120 <function>
3121 <name>GetMessageSize</name>
3122 <type>STRING</type>
3123 <documentation>
3124 <purpose>Get the size of the whole message headers and body.</purpose>
3125 <usage>Use this function to retrieve the size in bytes of the
3126 message headers and body without sending it.</usage>
3127 <returnvalue>An error message in case there was an error or an empty
3128 string otherwise. This return value may be safely ignored if the
3129 function parameters are set correctly.</returnvalue>
3130 </documentation>
3131 <argument>
3132 <name>message</name>
3133 <type>STRING</type>
3134 <out />
3135 <documentation>
3136 <purpose>Reference to an integer variable to store the size of the
3137 message headers and body.</purpose>
3138 </documentation>
3139 </argument>
3140 <do>
3141 {/metadocument}
3142 */
3143 Function GetMessageSize(&$size)
3144 {
3145 if(strlen($error=$this->GetMessage($message)))
3146 return($error);
3147 $size=strlen($message);
3148 return("");
3149 }
3150 /*
3151 {metadocument}
3152 </do>
3153 </function>
3154 {/metadocument}
3155 */
3156
3157 /*
3158 {metadocument}
3159 <function>
3160 <name>Mail</name>
3161 <type>BOOLEAN</type>
3162 <documentation>
3163 <purpose>Emulate the PHP <tt>mail()</tt> function by composing and
3164 sending a message given the same arguments.<paragraphbreak />
3165 This is mostly meant to provide a solution for sending messages
3166 with alternative delivery methods provided by this class
3167 sub-classes. It uses the same arguments as the PHP <tt>mail()</tt>
3168 function. Developers willing to use this alternative do not need to
3169 change much their scripts that already use the <tt>mail()</tt>
3170 function.</purpose>
3171 <usage>Use this function passing the same arguments as to PHP
3172 <tt><link>
3173 <data>mail()</data>
3174 <url>http://www.php.net/manual/en/function.mail.php</url>
3175 </link></tt> function.</usage>
3176 <returnvalue>If this function succeeds, it returns
3177 <tt><booleanvalue>1</booleanvalue></tt>.</returnvalue>
3178 </documentation>
3179 <argument>
3180 <name>to</name>
3181 <type>STRING</type>
3182 <documentation>
3183 <purpose>Recipient e-mail address.</purpose>
3184 </documentation>
3185 </argument>
3186 <argument>
3187 <name>subject</name>
3188 <type>STRING</type>
3189 <documentation>
3190 <purpose>Message subject.</purpose>
3191 </documentation>
3192 </argument>
3193 <argument>
3194 <name>message</name>
3195 <type>STRING</type>
3196 <documentation>
3197 <purpose>Message body.</purpose>
3198 </documentation>
3199 </argument>
3200 <argument>
3201 <name>additional_headers</name>
3202 <type>STRING</type>
3203 <defaultvalue></defaultvalue>
3204 <documentation>
3205 <purpose>Text string headers and the respective values. There
3206 should be one header and value per line with line breaks
3207 separating each line.</purpose>
3208 </documentation>
3209 </argument>
3210 <argument>
3211 <name>additional_parameters</name>
3212 <type>STRING</type>
3213 <defaultvalue></defaultvalue>
3214 <documentation>
3215 <purpose>Text string with additional parameters. In the original
3216 PHP <tt>mail()</tt> function these were actual switches to be
3217 passed in the sendmail program invocation command line. This
3218 function only supports the <tt>-f</tt> switch followed by an
3219 e-mail address meant to specify the message bounce return path
3220 address.</purpose>
3221 </documentation>
3222 </argument>
3223 <do>
3224 {/metadocument}
3225 */
3226 Function Mail($to, $subject, $message, $additional_headers="", $additional_parameters="")
3227 {
3228 $this->ResetMessage();
3229 $this->headers=array("To"=>$to,"Subject"=>$subject);
3230 $content_type="";
3231 while(strlen($additional_headers))
3232 {
3233 preg_match("/([^\r\n]+)(\r?\n)?(.*)\$/",$additional_headers,$matches);
3234 $header=$matches[1];
3235 $additional_headers=$matches[3];
3236 if(!preg_match("/^([^:]+):[ \t]+(.+)\$/",$header,$matches))
3237 {
3238 $this->error="invalid header \"$header\"";
3239 return(0);
3240 }
3241 if(strtolower($matches[1])=="content-type")
3242 {
3243 if(strlen($content_type))
3244 {
3245 $this->error="the content-type header was specified more than once.";
3246 return(0);
3247 }
3248 $content_type=$matches[2];
3249 }
3250 else
3251 $this->SetHeader($matches[1],$matches[2]);
3252 }
3253 if(strlen($additional_parameters))
3254 {
3255 if(preg_match("/^[ \t]*-f[ \t]*([^@]+@[^ \t]+)[ \t]*(.*)\$/", $additional_parameters, $matches))
3256 {
3257 if(!preg_match('/'.str_replace('/', '\\/', $this->email_regular_expression).'/i', $matches[1]))
3258 {
3259 $this->error="it was specified an invalid e-mail address for the additional parameter -f";
3260 return(0);
3261 }
3262 if(strlen($matches[2]))
3263 {
3264 $this->error="it were specified some additional parameters after -f e-mail address parameter that are not supported";
3265 return(0);
3266 }
3267 $this->SetHeader("Return-Path",$matches[1]);
3268 }
3269 else
3270 {
3271 $this->error="the additional parameters that were specified are not supported";
3272 return(0);
3273 }
3274 }
3275 if(strlen($content_type)==0)
3276 $content_type="text/plain";
3277 $definition=array(
3278 "Content-Type"=>$content_type,
3279 "DATA"=>$message
3280 );
3281 $this->CreateAndAddPart($definition);
3282 $this->Send();
3283 return(strlen($this->error)==0);
3284 }
3285 /*
3286 {metadocument}
3287 </do>
3288 </function>
3289 {/metadocument}
3290 */
3291
3292 Function ChangeBulkMail($on)
3293 {
3294 return(1);
3295 }
3296
3297 /*
3298 {metadocument}
3299 <function>
3300 <name>SetBulkMail</name>
3301 <type>BOOLEAN</type>
3302 <documentation>
3303 <purpose>Hint the class to adjust itself in order to send individual
3304 messages to many recipients more efficiently.</purpose>
3305 <usage>Call this function before starting sending messages to many
3306 recipients passing <booleanvalue>1</booleanvalue> to the
3307 <argumentlink>
3308 <function>SetBulkMail</function>
3309 <argument>on</argument>
3310 </argumentlink> argument. Then call this function again after the
3311 bulk mailing delivery has ended passing passing
3312 <booleanvalue>1</booleanvalue> to the <argumentlink>
3313 <function>SetBulkMail</function>
3314 <argument>on</argument>
3315 </argumentlink> argument.</usage>
3316 <returnvalue>If this function succeeds, it returns
3317 <tt><booleanvalue>1</booleanvalue></tt>.</returnvalue>
3318 </documentation>
3319 <argument>
3320 <name>on</name>
3321 <type>BOOLEAN</type>
3322 <documentation>
3323 <purpose>Boolean flag that indicates whether a bulk delivery is
3324 going to start if set to <booleanvalue>1</booleanvalue> or that
3325 the bulk delivery has ended if set to
3326 <booleanvalue>0</booleanvalue>.</purpose>
3327 </documentation>
3328 </argument>
3329 <do>
3330 {/metadocument}
3331 */
3332 Function SetBulkMail($on)
3333 {
3334 if(strlen($this->error))
3335 return(0);
3336 if(!$this->bulk_mail==!$on)
3337 return(1);
3338 if(!$this->ChangeBulkMail($on))
3339 return(0);
3340 $this->bulk_mail=!!$on;
3341 return(1);
3342 }
3343 /*
3344 {metadocument}
3345 </do>
3346 </function>
3347 {/metadocument}
3348 */
3349
3350 Function OpenMailing(&$mailing,&$mailing_properties)
3351 {
3352 if(strlen($this->error))
3353 return($this->error);
3354 if(!IsSet($mailing_properties["Name"])
3355 || strlen($mailing_properties["Name"])==0)
3356 return($this->OutputError("it was not specified a valid mailing Name"));
3357 if(!IsSet($mailing_properties["Return-Path"])
3358 || strlen($mailing_properties["Return-Path"])==0)
3359 return($this->OutputError("it was not specified a valid mailing Return-Path"));
3360 $separator="";
3361 $directory_separator=(defined("DIRECTORY_SEPARATOR") ? DIRECTORY_SEPARATOR : ((defined("PHP_OS") && !strcmp(substr(PHP_OS,0,3),"WIN")) ? "\\" : "/"));
3362 $length=strlen($this->mailing_path);
3363 if($length)
3364 {
3365 if($this->mailing_path[$length-1]!=$directory_separator)
3366 $separator=$directory_separator;
3367 }
3368 $base_path=$this->mailing_path.$separator.$mailing_properties["Name"];
3369 if($this->body_parts==0)
3370 return($this->OutputError("message has no body parts"));
3371 $line_break="\n";
3372 $headers=$this->headers;
3373 if(strlen($this->mailer))
3374 $headers["X-Mailer"]=$this->mailer;
3375 $headers["MIME-Version"]="1.0";
3376 if(strlen($error=$this->GetPartHeaders($headers,$this->body)))
3377 return($error);
3378 if(!($header_file=@fopen($base_path.".h","wb")))
3379 return($this->OutputPHPError("could not open mailing headers file ".$base_path.".h", $php_errormsg));
3380 for($header=0,Reset($headers);$header<count($headers);Next($headers),++$header)
3381 {
3382 $header_name=Key($headers);
3383 if(!@fwrite($header_file,$header_name.": ".$headers[$header_name].$line_break))
3384 {
3385 fclose($header_file);
3386 return($this->OutputPHPError("could not write to the mailing headers file ".$base_path.".h", $php_errormsg));
3387 }
3388 }
3389 if(!@fflush($header_file))
3390 {
3391 fclose($header_file);
3392 @unlink($base_path.".h");
3393 return($this->OutputPHPError("could not write to the mailing headers file ".$base_path.".h", $php_errormsg));
3394 }
3395 fclose($header_file);
3396 if(strlen($error=$this->GetPartBody($body,$this->body)))
3397 {
3398 @unlink($base_path.".h");
3399 return($error);
3400 }
3401 if(!($body_file=@fopen($base_path.".b","wb")))
3402 {
3403 @unlink($base_path.".h");
3404 return($this->OutputPHPError("could not open mailing body file ".$base_path.".b", $php_errormsg));
3405 }
3406 if(!@fwrite($body_file,$body)
3407 || !@fflush($body_file))
3408 {
3409 fclose($body_file);
3410 @unlink($base_path.".b");
3411 @unlink($base_path.".h");
3412 return($this->OutputPHPError("could not write to the mailing body file ".$base_path.".b", $php_errormsg));
3413 }
3414 fclose($body_file);
3415 if(!($envelope=@fopen($base_path.".e","wb")))
3416 {
3417 @unlink($base_path.".b");
3418 @unlink($base_path.".h");
3419 return($this->OutputPHPError("could not open mailing envelope file ".$base_path.".e", $php_errormsg));
3420 }
3421 if(!@fwrite($envelope,"F".$mailing_properties["Return-Path"].chr(0))
3422 || !@fflush($envelope))
3423 {
3424 @fclose($envelope);
3425 @unlink($base_path.".e");
3426 @unlink($base_path.".b");
3427 @unlink($base_path.".h");
3428 return($this->OutputPHPError("could not write to the return path to the mailing envelope file ".$base_path.".e", $php_errormsg));
3429 }
3430 $mailing=++$this->last_mailing;
3431 $this->mailings[$mailing]=array(
3432 "Envelope"=>$envelope,
3433 "BasePath"=>$base_path
3434 );
3435 return("");
3436 }
3437
3438 Function AddMailingRecipient($mailing,&$recipient_properties)
3439 {
3440 if(strlen($this->error))
3441 return($this->error);
3442 if(!IsSet($this->mailings[$mailing]))
3443 return($this->OutputError("it was not specified a valid mailing"));
3444 if(!IsSet($recipient_properties["Address"])
3445 || strlen($recipient_properties["Address"])==0)
3446 return($this->OutputError("it was not specified a valid mailing recipient Address"));
3447 if(!@fwrite($this->mailings[$mailing]["Envelope"],"T".$recipient_properties["Address"].chr(0)))
3448 return($this->OutputPHPError("could not write recipient address to the mailing envelope file", $php_errormsg));
3449 return("");
3450 }
3451
3452 Function EndMailing($mailing)
3453 {
3454 if(strlen($this->error))
3455 return($this->error);
3456 if(!IsSet($this->mailings[$mailing]))
3457 return($this->OutputError("it was not specified a valid mailing"));
3458 if(!IsSet($this->mailings[$mailing]["Envelope"]))
3459 return($this->OutputError("the mailing was already ended"));
3460 if(!@fwrite($this->mailings[$mailing]["Envelope"],chr(0))
3461 || !@fflush($this->mailings[$mailing]["Envelope"]))
3462 return($this->OutputPHPError("could not end writing to the mailing envelope file", $php_errormsg));
3463 fclose($this->mailings[$mailing]["Envelope"]);
3464 Unset($this->mailings[$mailing]["Envelope"]);
3465 return("");
3466 }
3467
3468 Function SendMailing($mailing)
3469 {
3470 if(strlen($this->error))
3471 return($this->error);
3472 if(!IsSet($this->mailings[$mailing]))
3473 return($this->OutputError("it was not specified a valid mailing"));
3474 if(IsSet($this->mailings[$mailing]["Envelope"]))
3475 return($this->OutputError("the mailing was not yet ended"));
3476 $this->ResetMessage();
3477 $base_path=$this->mailings[$mailing]["BasePath"];
3478 if(GetType($header_lines=@File($base_path.".h"))!="array")
3479 return($this->OutputPHPError("could not read the mailing headers file ".$base_path.".h", $php_errormsg));
3480 for($line=0;$line<count($header_lines);++$line)
3481 {
3482 $header_name=$this->Tokenize($header_lines[$line],": ");
3483 $this->headers[$header_name]=trim($this->Tokenize("\n"));
3484 }
3485 if(!($envelope_file=@fopen($base_path.".e","rb")))
3486 return($this->OutputPHPError("could not open the mailing envelope file ".$base_path.".e", $php_errormsg));
3487 for($bcc=$data="",$position=0;!feof($envelope_file) || strlen($data);)
3488 {
3489 if(GetType($break=strpos($data,chr(0),$position))!="integer")
3490 {
3491 if(GetType($chunk=@fread($envelope_file,$this->file_buffer_length))!="string")
3492 {
3493 fclose($envelope_file);
3494 return($this->OutputPHPError("could not read the mailing envelop file ".$base_path.".e", $php_errormsg));
3495 }
3496 $data=substr($data,$position).$chunk;
3497 $position=0;
3498 continue;
3499 }
3500 if($break==$position)
3501 break;
3502 switch($data[$position])
3503 {
3504 case "F":
3505 $this->headers["Return-Path"]=substr($data,$position+1,$break-$position-1);
3506 break;
3507 case "T":
3508 $bcc.=(strlen($bcc)==0 ? "" : ", ").substr($data,$position+1,$break-$position-1);
3509 break;
3510 default:
3511 return($this->OutputError("invalid mailing envelope file ".$base_path.".e"));
3512 }
3513 $position=$break+1;
3514 }
3515 fclose($envelope_file);
3516 if(strlen($bcc)==0)
3517 return($this->OutputError("the mailing envelop file ".$base_path.".e does not contain any recipients"));
3518 $this->headers["Bcc"]=$bcc;
3519 if(!($body_file=@fopen($base_path.".b","rb")))
3520 return($this->OutputPHPError("could not open the mailing body file ".$base_path.".b", $php_errormsg));
3521 for($data="";!feof($body_file);)
3522 {
3523 if(GetType($chunk=@fread($body_file,$this->file_buffer_length))!="string")
3524 {
3525 fclose($body_file);
3526 return($this->OutputPHPError("could not read the mailing body file ".$base_path.".b", $php_errormsg));
3527 }
3528 $data.=$chunk;
3529 }
3530 fclose($body_file);
3531 if(strlen($error=$this->StartSendingMessage()))
3532 return($error);
3533 if(strlen($error=$this->SendMessageHeaders($this->headers))==0
3534 && strlen($error=$this->SendMessageBody($data))==0)
3535 $error=$this->EndSendingMessage();
3536 $this->StopSendingMessage();
3537 return($error);
3538 }
3539 };
3540
3541 /*
3542
3543 {metadocument}
3544 </class>
3545 {/metadocument}
3546
3547 */
3548
3549 ?>