1<?php 2/* 3 * rfc822_addresses.php 4 * 5 * @(#) $Id: rfc822_addresses.php,v 1.13 2010/04/08 20:09:23 mlemos Exp $ 6 * 7 */ 8 9/* 10{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?> 11<class> 12 13 <package>net.manuellemos.mimeparser</package> 14 15 <version>@(#) $Id: rfc822_addresses.php,v 1.13 2010/04/08 20:09:23 mlemos Exp $</version> 16 <copyright>Copyright � (C) Manuel Lemos 2006 - 2008</copyright> 17 <title>RFC 822 e-mail addresses parser</title> 18 <author>Manuel Lemos</author> 19 <authoraddress>mlemos-at-acm.org</authoraddress> 20 21 <documentation> 22 <idiom>en</idiom> 23 <purpose>Parse e-mail addresses from headers of <link> 24 <url>http://www.ietf.org/rfc/rfc822.txt</url> 25 <data>RFC 822</data> 26 </link> compliant e-mail messages.</purpose> 27 <usage>Use the function <functionlink>ParseAddressList</functionlink> 28 function to retrieve the list of e-mail addresses contained in 29 e-mail message headers like <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt> 30 or <tt>Bcc</tt>.</usage> 31 </documentation> 32 33{/metadocument} 34*/ 35 36class rfc822_addresses_class 37{ 38 /* Private variables */ 39 40 var $v = ''; 41 42 /* Public variables */ 43 44/* 45{metadocument} 46 <variable> 47 <name>error</name> 48 <type>STRING</type> 49 <value></value> 50 <documentation> 51 <purpose>Store the message that is returned when an error 52 occurs.</purpose> 53 <usage>Check this variable to understand what happened when a call to 54 any of the class functions has failed.<paragraphbreak /> 55 This class uses cumulative error handling. This means that if one 56 class functions that may fail is called and this variable was 57 already set to an error message due to a failure in a previous call 58 to the same or other function, the function will also fail and does 59 not do anything.<paragraphbreak /> 60 This allows programs using this class to safely call several 61 functions that may fail and only check the failure condition after 62 the last function call.<paragraphbreak /> 63 Just set this variable to an empty string to clear the error 64 condition.</usage> 65 </documentation> 66 </variable> 67{/metadocument} 68*/ 69 var $error = ''; 70 71/* 72{metadocument} 73 <variable> 74 <name>error_position</name> 75 <type>INTEGER</type> 76 <value>-1</value> 77 <documentation> 78 <purpose>Point to the position of the message data or file that 79 refers to the last error that occurred.</purpose> 80 <usage>Check this variable to determine the relevant position of the 81 message when a parsing error occurs.</usage> 82 </documentation> 83 </variable> 84{/metadocument} 85*/ 86 var $error_position = -1; 87 88/* 89{metadocument} 90 <variable> 91 <name>ignore_syntax_errors</name> 92 <type>BOOLEAN</type> 93 <value>1</value> 94 <documentation> 95 <purpose>Specify whether the class should ignore syntax errors in 96 malformed addresses.</purpose> 97 <usage>Set this variable to <booleanvalue>0</booleanvalue> if it is 98 necessary to verify whether message data may be corrupted due to 99 to eventual bugs in the program that generated the 100 message.<paragraphbreak /> 101 Currently the class only ignores some types of syntax errors. 102 Other syntax errors may still cause the 103 <functionlink>ParseAddressList</functionlink> to fail.</usage> 104 </documentation> 105 </variable> 106{/metadocument} 107*/ 108 var $ignore_syntax_errors=1; 109 110/* 111{metadocument} 112 <variable> 113 <name>warnings</name> 114 <type>HASH</type> 115 <value></value> 116 <documentation> 117 <purpose>Return a list of positions of the original message that 118 contain syntax errors.</purpose> 119 <usage>Check this variable to retrieve eventual message syntax 120 errors that were ignored when the 121 <variablelink>ignore_syntax_errors</variablelink> is set to 122 <booleanvalue>1</booleanvalue>.<paragraphbreak /> 123 The indexes of this array are the positions of the errors. The 124 array values are the corresponding syntax error messages.</usage> 125 </documentation> 126 </variable> 127{/metadocument} 128*/ 129 var $warnings=array(); 130 131 /* Private functions */ 132 133 Function SetError($error) 134 { 135 $this->error = $error; 136 return(0); 137 } 138 139 Function SetPositionedError($error, $position) 140 { 141 $this->error_position = $position; 142 return($this->SetError($error)); 143 } 144 145 Function SetWarning($warning, $position) 146 { 147 $this->warnings[$position]=$warning; 148 return(1); 149 } 150 151 Function SetPositionedWarning($error, $position) 152 { 153 if(!$this->ignore_syntax_errors) 154 return($this->SetPositionedError($error, $position)); 155 return($this->SetWarning($error, $position)); 156 } 157 158 Function QDecode($p, &$value, &$encoding) 159 { 160 $encoding = $charset = null; 161 $s = 0; 162 $decoded = ''; 163 $l = strlen($value); 164 while($s < $l) 165 { 166 if(GetType($q = strpos($value, '=?', $s)) != 'integer') 167 { 168 if($s == 0) 169 return(1); 170 if($s < $l) 171 $decoded .= substr($value, $s); 172 break; 173 } 174 if($s < $q) 175 $decoded .= substr($value, $s, $q - $s); 176 $q += 2; 177 if(GetType($c = strpos($value, '?', $q)) != 'integer' 178 || $q == $c) 179 return($this->SetPositionedWarning('invalid Q-encoding character set', $p + $q)); 180 if(IsSet($charset)) 181 { 182 $another_charset = strtolower(substr($value, $q, $c - $q)); 183 if(strcmp($charset, $another_charset) 184 && strcmp($another_charset, 'ascii')) 185 return($this->SetWarning('it is not possible to decode an encoded value using mixed character sets into a single value', $p + $q)); 186 } 187 else 188 { 189 $charset = strtolower(substr($value, $q, $c - $q)); 190 if(!strcmp($charset, 'ascii')) 191 $charset = null; 192 } 193 ++$c; 194 if(GetType($t = strpos($value, '?', $c)) != 'integer' 195 || $c==$t) 196 return($this->SetPositionedWarning('invalid Q-encoding type', $p + $c)); 197 $type = strtolower(substr($value, $c, $t - $c)); 198 ++$t; 199 if(GetType($e = strpos($value, '?=', $t)) != 'integer') 200 return($this->SetPositionedWarning('invalid Q-encoding encoded data', $p + $e)); 201 switch($type) 202 { 203 case 'q': 204 for($s = $t; $s<$e;) 205 { 206 switch($b = $value[$s]) 207 { 208 case '=': 209 $h = HexDec($hex = strtolower(substr($value, $s + 1, 2))); 210 if($s + 3 > $e 211 || strcmp(sprintf('%02x', $h), $hex)) 212 return($this->SetPositionedWarning('invalid Q-encoding q encoded data', $p + $s)); 213 $decoded .= chr($h); 214 $s += 3; 215 break; 216 217 case '_': 218 $decoded .= ' '; 219 ++$s; 220 break; 221 222 default: 223 $decoded .= $b; 224 ++$s; 225 } 226 } 227 break; 228 229 case 'b': 230 if($e <= $t 231 || strlen($binary = base64_decode($data = substr($value, $t, $e - $t))) == 0 232 || GetType($binary) != 'string') 233 return($this->SetPositionedWarning('invalid Q-encoding b encoded data', $p + $t)); 234 $decoded .= $binary; 235 $s = $e; 236 break; 237 238 default: 239 return($this->SetPositionedWarning('Q-encoding '.$type.' is not yet supported', $p + $c)); 240 } 241 $s += 2; 242 } 243 $value = $decoded; 244 $encoding = $charset; 245 return(1); 246 } 247 248 Function ParseCText(&$p, &$c_text) 249 { 250 $c_text = null; 251 $v = $this->v; 252 if($p<strlen($v) 253 && GetType(strchr("\t\r\n ()\\\0", $c = $v[$p])) != 'string' 254 && Ord($c)<128) 255 { 256 $c_text = $c; 257 ++$p; 258 } 259 return(1); 260 } 261 262 Function ParseQText(&$p, &$q_text) 263 { 264 $q_text = null; 265 $v = $this->v; 266 if($p>strlen($v) 267 || GetType(strchr("\t\r\n \"\\\0", $c = $v[$p])) == 'string') 268 return(1); 269 if(Ord($c) >= 128) 270 { 271 if(!$this->ignore_syntax_errors) 272 return(1); 273 $this->SetPositionedWarning('it was used an unencoded 8 bit character', $p); 274 } 275 $q_text = $c; 276 ++$p; 277 return(1); 278 } 279 280 Function ParseQuotedPair(&$p, &$quoted_pair) 281 { 282 $quoted_pair = null; 283 $v = $this->v; 284 $l = strlen($v); 285 if($p+1 < $l 286 && !strcmp($v[$p], '\\') 287 && GetType(strchr("\r\n\0", $c = $v[$p + 1])) != 'string' 288 && Ord($c)<128) 289 { 290 $quoted_pair = $c; 291 $p += 2; 292 } 293 return(1); 294 } 295 296 Function ParseCContent(&$p, &$c_content) 297 { 298 $c_content = null; 299 $c = $p; 300 if(!$this->ParseQuotedPair($c, $content)) 301 return(0); 302 if(!IsSet($content)) 303 { 304 if(!$this->ParseCText($c, $content)) 305 return(0); 306 if(!IsSet($content)) 307 { 308 if(!$this->ParseComment($c, $content)) 309 return(0); 310 if(!IsSet($content)) 311 return(1); 312 } 313 } 314 $c_content = $content; 315 $p = $c; 316 return(1); 317 } 318 319 Function SkipWhiteSpace(&$p) 320 { 321 $v = $this->v; 322 $l = strlen($v); 323 for(;$p<$l; ++$p) 324 { 325 switch($v[$p]) 326 { 327 case ' ': 328 case "\n": 329 case "\r": 330 case "\t": 331 break; 332 default: 333 return(1); 334 } 335 } 336 return(1); 337 } 338 339 Function ParseComment(&$p, &$comment) 340 { 341 $comment = null; 342 $v = $this->v; 343 $l = strlen($v); 344 $c = $p; 345 if($c >= $l 346 || strcmp($v[$c], '(')) 347 return(1); 348 ++$c; 349 for(; $c < $l;) 350 { 351 if(!$this->SkipWhiteSpace($c)) 352 return(0); 353 if(!$this->ParseCContent($c, $c_content)) 354 return(0); 355 if(!IsSet($c_content)) 356 break; 357 } 358 if(!$this->SkipWhiteSpace($c)) 359 return(0); 360 if($c >= $l 361 || strcmp($v[$c], ')')) 362 return(1); 363 ++$c; 364 $comment = substr($v, $p, $c - $p); 365 $p = $c; 366 return(1); 367 } 368 369 Function SkipCommentGetWhiteSpace(&$p, &$space) 370 { 371 $v = $this->v; 372 $l = strlen($v); 373 for($space = '';$p<$l;) 374 { 375 switch($w = $v[$p]) 376 { 377 case ' ': 378 case "\n": 379 case "\r": 380 case "\t": 381 ++$p; 382 $space .= $w; 383 break; 384 case '(': 385 if(!$this->ParseComment($p, $comment)) 386 return(0); 387 default: 388 return(1); 389 } 390 } 391 return(1); 392 } 393 394 Function SkipCommentWhiteSpace(&$p) 395 { 396 $v = $this->v; 397 $l = strlen($v); 398 for(;$p<$l;) 399 { 400 switch($w = $v[$p]) 401 { 402 case ' ': 403 case "\n": 404 case "\r": 405 case "\t": 406 ++$p; 407 break; 408 case '(': 409 if(!$this->ParseComment($p, $comment)) 410 return(0); 411 default: 412 return(1); 413 } 414 } 415 return(1); 416 } 417 418 Function ParseQContent(&$p, &$q_content) 419 { 420 $q_content = null; 421 $q = $p; 422 if(!$this->ParseQuotedPair($q, $content)) 423 return(0); 424 if(!IsSet($content)) 425 { 426 if(!$this->ParseQText($q, $content)) 427 return(0); 428 if(!IsSet($content)) 429 return(1); 430 } 431 $q_content = $content; 432 $p = $q; 433 return(1); 434 } 435 436 Function ParseAtom(&$p, &$atom, $dot) 437 { 438 $atom = null; 439 $v = $this->v; 440 $l = strlen($v); 441 $a = $p; 442 if(!$this->SkipCommentGetWhiteSpace($a, $space)) 443 return(0); 444 $match = '/^([-'.($dot ? '.' : '').'A-Za-z0-9!#$&\'*+\\/=?^_{|}~]+)/'; 445 for($s = $a;$a < $l;) 446 { 447 if(preg_match($match, substr($this->v, $a), $m)) 448 $a += strlen($m[1]); 449 elseif(Ord($v[$a]) < 128) 450 break; 451 elseif(!$this->SetPositionedWarning('it was used an unencoded 8 bit character', $a)) 452 return(0); 453 else 454 ++$a; 455 } 456 if($s == $a) 457 return(1); 458 $atom = $space.substr($this->v, $s, $a - $s); 459 if(!$this->SkipCommentGetWhiteSpace($a, $space)) 460 return(0); 461 $atom .= $space; 462 $p = $a; 463 return(1); 464 } 465 466 Function ParseQuotedString(&$p, &$quoted_string) 467 { 468 $quoted_string = null; 469 $v = $this->v; 470 $l = strlen($v); 471 $s = $p; 472 if(!$this->SkipCommentWhiteSpace($s)) 473 return(0); 474 if($s >= $l 475 || strcmp($v[$s], '"')) 476 return(1); 477 ++$s; 478 for($string = '';$s < $l;) 479 { 480 $w = $s; 481 if(!$this->SkipWhiteSpace($s)) 482 return(0); 483 if($w != $s) 484 $string .= substr($v, $w, $s - $w); 485 if(!$this->ParseQContent($s, $q_content)) 486 return(0); 487 if(!IsSet($q_content)) 488 break; 489 $string .= $q_content; 490 } 491 $w = $s; 492 if(!$this->SkipWhiteSpace($s)) 493 return(0); 494 if($w != $s) 495 $string .= substr($v, $w, $s - $w); 496 if($s >= $l 497 || strcmp($v[$s], '"')) 498 return(1); 499 ++$s; 500 if(!$this->SkipCommentWhiteSpace($s)) 501 return(0); 502 $quoted_string = $string; 503 $p = $s; 504 return(1); 505 } 506 507 Function ParseWord(&$p, &$word) 508 { 509 $word = null; 510 if(!$this->ParseQuotedString($p, $word)) 511 return(0); 512 if(IsSet($word)) 513 return(1); 514 if(!$this->ParseAtom($p, $word, 0)) 515 return(0); 516 return(1); 517 } 518 519 Function ParseObsPhrase(&$p, &$obs_phrase) 520 { 521 $obs_phrase = null; 522 $v = $this->v; 523 $l = strlen($v); 524 $ph = $p; 525 if(!$this->ParseWord($ph, $word)) 526 return(0); 527 $string = $word; 528 for(;;) 529 { 530 if(!$this->ParseWord($ph, $word)) 531 return(0); 532 if(IsSet($word)) 533 { 534 $string .= $word; 535 continue; 536 } 537 $w = $ph; 538 if(!$this->SkipCommentGetWhiteSpace($ph, $space)) 539 return(0); 540 if($w != $ph) 541 { 542 $string .= $space; 543 continue; 544 } 545 if($ph >= $l 546 || strcmp($v[$ph], '.')) 547 break; 548 $string .= '.'; 549 ++$ph; 550 } 551 $obs_phrase = $string; 552 $p = $ph; 553 return(1); 554 } 555 556 Function ParsePhrase(&$p, &$phrase) 557 { 558 $phrase = null; 559 if(!$this->ParseObsPhrase($p, $phrase)) 560 return(0); 561 if(IsSet($phrase)) 562 return(1); 563 $ph = $p; 564 if(!$this->ParseWord($ph, $word)) 565 return(0); 566 $string = $word; 567 for(;;) 568 { 569 if(!$this->ParseWord($ph, $word)) 570 return(0); 571 if(!IsSet($word)) 572 break; 573 $string .= $word; 574 } 575 $phrase = $string; 576 $p = $ph; 577 return(1); 578 } 579 580 Function ParseAddrSpec(&$p, &$addr_spec) 581 { 582 $addr_spec = null; 583 $v = $this->v; 584 $l = strlen($v); 585 $a = $p; 586 if(!$this->ParseQuotedString($a, $local_part)) 587 return(0); 588 if(!IsSet($local_part)) 589 { 590 if(!$this->ParseAtom($a, $local_part, 1)) 591 return(0); 592 $local_part = trim($local_part); 593 } 594 if($a >= $l 595 || strcmp($v[$a], '@')) 596 return(1); 597 ++$a; 598 if(!$this->ParseAtom($a, $domain, 1)) 599 return(0); 600 if(!IsSet($domain)) 601 return(1); 602 $addr_spec = $local_part.'@'.trim($domain); 603 $p = $a; 604 return(1); 605 } 606 607 Function ParseAngleAddr(&$p, &$addr) 608 { 609 $addr = null; 610 $v = $this->v; 611 $l = strlen($v); 612 $a = $p; 613 if(!$this->SkipCommentWhiteSpace($a)) 614 return(0); 615 if($a >= $l 616 || strcmp($v[$a], '<')) 617 return(1); 618 ++$a; 619 if(!$this->ParseAddrSpec($a, $addr_spec)) 620 return(0); 621 if($a >= $l 622 || strcmp($v[$a], '>')) 623 return(1); 624 ++$a; 625 if(!$this->SkipCommentWhiteSpace($a)) 626 return(0); 627 $addr = $addr_spec; 628 $p = $a; 629 return(1); 630 } 631 632 Function ParseName(&$p, &$address) 633 { 634 $address = null; 635 $a = $p; 636 if(!$this->ParsePhrase($a, $display_name)) 637 return(0); 638 if(IsSet($display_name)) 639 { 640 if(!$this->QDecode($p, $display_name, $encoding)) 641 return(0); 642 $address['name'] = trim($display_name); 643 if(IsSet($encoding)) 644 $address['encoding'] = $encoding; 645 } 646 $p = $a; 647 return(1); 648 } 649 650 Function ParseNameAddr(&$p, &$address) 651 { 652 $address = null; 653 $a = $p; 654 if(!$this->ParsePhrase($a, $display_name)) 655 return(0); 656 if(!$this->ParseAngleAddr($a, $addr)) 657 return(0); 658 if(!IsSet($addr)) 659 return(1); 660 $address = array('address'=>$addr); 661 if(IsSet($display_name)) 662 { 663 if(!$this->QDecode($p, $display_name, $encoding)) 664 return(0); 665 $address['name'] = trim($display_name); 666 if(IsSet($encoding)) 667 $address['encoding'] = $encoding; 668 } 669 $p = $a; 670 return(1); 671 } 672 673 Function ParseAddrNameAddr(&$p, &$address) 674 { 675 $address = null; 676 $a = $p; 677 if(!$this->ParseAddrSpec($a, $display_name)) 678 return(0); 679 if(!IsSet($display_name)) 680 return(1); 681 if(!$this->ParseAngleAddr($a, $addr)) 682 return(0); 683 if(!IsSet($addr)) 684 return(1); 685 if(!$this->QDecode($p, $display_name, $encoding)) 686 return(0); 687 $address = array( 688 'address'=>$addr, 689 'name' => trim($display_name) 690 ); 691 if(IsSet($encoding)) 692 $address['encoding'] = $encoding; 693 $p = $a; 694 return(1); 695 } 696 697 Function ParseMailbox(&$p, &$address) 698 { 699 $address = null; 700 if($this->ignore_syntax_errors) 701 { 702 $a = $p; 703 if(!$this->ParseAddrNameAddr($p, $address)) 704 return(0); 705 if(IsSet($address)) 706 return($this->SetPositionedWarning('it was specified an unquoted address as name', $a)); 707 } 708 if(!$this->ParseNameAddr($p, $address)) 709 return(0); 710 if(IsSet($address)) 711 return(1); 712 if(!$this->ParseAddrSpec($p, $addr_spec)) 713 return(0); 714 if(IsSet($addr_spec)) 715 { 716 $address = array('address'=>$addr_spec); 717 return(1); 718 } 719 $a = $p; 720 if($this->ignore_syntax_errors 721 && $this->ParseName($p, $address) 722 && IsSet($address)) 723 return($this->SetPositionedWarning('it was specified a name without an address', $a)); 724 return(1); 725 } 726 727 Function ParseMailboxGroup(&$p, &$mailbox_group) 728 { 729 $v = $this->v; 730 $l = strlen($v); 731 $g = $p; 732 if(!$this->ParseMailbox($g, $address)) 733 return(0); 734 if(!IsSet($address)) 735 return(1); 736 $addresses = array($address); 737 for(;$g < $l;) 738 { 739 if(strcmp($v[$g], ',')) 740 break; 741 ++$g; 742 if(!$this->ParseMailbox($g, $address)) 743 return(0); 744 if(!IsSet($address)) 745 return(1); 746 $addresses[] = $address; 747 } 748 $mailbox_group = $addresses; 749 $p = $g; 750 return(1); 751 } 752 753 Function ParseGroup(&$p, &$address) 754 { 755 $address = null; 756 $v = $this->v; 757 $l = strlen($v); 758 $g = $p; 759 if(!$this->ParsePhrase($g, $display_name)) 760 return(0); 761 if(!IsSet($display_name) 762 || $g >= $l 763 || strcmp($v[$g], ':')) 764 return(1); 765 ++$g; 766 if(!$this->ParseMailboxGroup($g, $mailbox_group)) 767 return(0); 768 if(!IsSet($mailbox_group)) 769 { 770 if(!$this->SkipCommentWhiteSpace($g)) 771 return(0); 772 $mailbox_group = array(); 773 } 774 if($g >= $l 775 || strcmp($v[$g], ';')) 776 return(1); 777 $c = ++$g; 778 if($this->SkipCommentWhiteSpace($g) 779 && $g > $c 780 && !$this->SetPositionedWarning('it were used invalid comments after a group of addresses', $c)) 781 return(0); 782 if(!$this->QDecode($p, $display_name, $encoding)) 783 return(0); 784 $address = array( 785 'name'=>$display_name, 786 'group'=>$mailbox_group 787 ); 788 if(IsSet($encoding)) 789 $address['encoding'] = $encoding; 790 $p = $g; 791 return(1); 792 } 793 794 Function ParseAddress(&$p, &$address) 795 { 796 $address = null; 797 if(!$this->ParseGroup($p, $address)) 798 return(0); 799 if(!IsSet($address)) 800 { 801 if(!$this->ParseMailbox($p, $address)) 802 return(0); 803 } 804 return(1); 805 } 806 807 /* Public functions */ 808 809/* 810{metadocument} 811 <function> 812 <name>ParseAddressList</name> 813 <type>BOOLEAN</type> 814 <documentation> 815 <purpose>Parse and extract e-mail addresses eventually from headers 816 of an e-mail message.</purpose> 817 <usage>Pass a string value with a list of e-mail addresses to the 818 <argumentlink> 819 <function>ParseAddressList</function> 820 <argument>value</argument> 821 </argumentlink>. The <argumentlink> 822 <function>ParseAddressList</function> 823 <argument>addresses</argument> 824 </argumentlink> returns the list of e-mail addresses found.</usage> 825 <returnvalue>This function returns <booleanvalue>1</booleanvalue> if 826 the specified value is parsed successfully. Otherwise, 827 check the variables <variablelink>error</variablelink> and 828 <variablelink>error_position</variablelink> to determine what 829 error occurred and the relevant value position.</returnvalue> 830 </documentation> 831 <argument> 832 <name>value</name> 833 <type>STRING</type> 834 <documentation> 835 <purpose>String with a list of e-mail addresses to parse.</purpose> 836 </documentation> 837 </argument> 838 <argument> 839 <name>addresses</name> 840 <type>ARRAY</type> 841 <out /> 842 <documentation> 843 <purpose>Return the list of parsed e-mail addresses. 844 Each entry in the list is an associative array.<paragraphbreak /> 845 For normal addresses, this associative array has the entry 846 <stringvalue>address</stringvalue> set to the e-mail address. 847 If the address has an associated name, it is stored in the 848 entry <stringvalue>name</stringvalue>.<paragraphbreak /> 849 For address groups, there is the entry 850 <stringvalue>name</stringvalue>. 851 The group addresses list are stored in the entry 852 <stringvalue>group</stringvalue> as an array. The structure of 853 the group addresses list array is the same as this addresses 854 list array argument.</purpose> 855 </documentation> 856 </argument> 857 <do> 858{/metadocument} 859*/ 860 Function ParseAddressList($value, &$addresses) 861 { 862 $this->warnings = array(); 863 $addresses = array(); 864 $this->v = $v = $value; 865 $l = strlen($v); 866 $p = 0; 867 if(!$this->ParseAddress($p, $address)) 868 return(0); 869 if(!IsSet($address)) 870 return($this->SetPositionedError('it was not specified a valid address', $p)); 871 $addresses[] = $address; 872 while($p < $l) 873 { 874 if(strcmp($v[$p], ',') 875 && !$this->SetPositionedWarning('multiple addresses must be separated by commas: ', $p)) 876 return(0); 877 ++$p; 878 if(!$this->ParseAddress($p, $address)) 879 return(0); 880 if(!IsSet($address)) 881 return($this->SetPositionedError('it was not specified a valid address after comma', $p)); 882 $addresses[] = $address; 883 } 884 return(1); 885 } 886/* 887{metadocument} 888 </do> 889 </function> 890{/metadocument} 891*/ 892 893}; 894 895/* 896 897{metadocument} 898</class> 899{/metadocument} 900 901*/ 902 903?>