├── .travis.yml ├── QdmailComponent.php ├── QdsmtpComponent.php ├── README.md ├── Tests ├── QdmailReceiverTest.php └── QdmailTest.php ├── phpunit.xml ├── qdmail.php ├── qdmail_receiver.php └── qdsmtp.php /.travis.yml: -------------------------------------------------------------------------------- 1 | # see http://about.travis-ci.org/docs/user/languages/php/ for more hints 2 | language: php 3 | 4 | matrix: 5 | include: 6 | - php: 5.2 7 | dist: precise 8 | script: phpunit --strict --configuration phpunit.xml --coverage-text 9 | - php: 5.3 10 | dist: precise 11 | script: phpunit --strict-coverage --strict-global-state --configuration phpunit.xml --coverage-text 12 | - php: 5.4 13 | dist: trusty 14 | script: phpunit --strict-coverage --strict-global-state --configuration phpunit.xml --coverage-text 15 | - php: 5.5 16 | dist: trusty 17 | script: phpunit --strict-coverage --strict-global-state --configuration phpunit.xml --coverage-text 18 | - php: 5.6 19 | script: phpunit --strict-coverage --strict-global-state --configuration phpunit.xml --coverage-text 20 | 21 | notifications: 22 | email: false 23 | 24 | 25 | -------------------------------------------------------------------------------- /QdsmtpComponent.php: -------------------------------------------------------------------------------- 1 | error; 47 | } 48 | if( is_null( $lf ) ){ 49 | $lf = $this->log_LFC ; 50 | } 51 | if( !is_array( $error ) ){ 52 | $error = array( $error ); 53 | } 54 | $out = null ; 55 | foreach($error as $mes){ 56 | $out .= $this->name . ' error: ' . trim( $mes ) . $lf ; 57 | } 58 | if( $this->error_display && $display ){ 59 | $_out = str_replace( $lf ,'
' . $lf , $out ); 60 | echo $_out ; 61 | } 62 | return $out ; 63 | } 64 | 65 | function errorGather( $message = null , $line = null){ 66 | 67 | if( !is_null( $message ) && !$this->error_ignore){ 68 | if( !is_null( $line ) ){ 69 | $message .= ' line -> '.$line; 70 | } 71 | $this->error[] = $message ; 72 | }elseif( 0 === count( $this->error ) ){ 73 | return true; 74 | }elseif( $this->error_ignore ){ 75 | return false; 76 | } 77 | 78 | $er = $this->errorRender(); 79 | $this->error_stack = array_merge( $this->error_stack , $this->error ); 80 | $this->error = array(); 81 | if( !$this->logWrite( 'error' , $er )){ 82 | $this->error_stack = array_merge( $this->error_stack , $this->error ); 83 | } 84 | return false; 85 | } 86 | 87 | function logWrite( $type , $message ){ 88 | $tp = ('error' == $type) ? false:true; 89 | $level = $tp ? $this->log_level:$this->errorlog_level; 90 | if( 0 == $level ){ 91 | return true; 92 | } 93 | $filename = $tp ? $this->log_filename:$this->errorlog_filename; 94 | $ap = $tp ? $this->log_append:$this->errorlog_append; 95 | $fp = fopen( $filename , $ap ); 96 | if( !is_resource( $fp ) ){ 97 | $this->error[]='file open error at logWrite() line->'.__LINE__; 98 | return false; 99 | } 100 | $spacer = $tp ? $this->log_LFC : $this->log_LFC ; 101 | fwrite( $fp , 102 | date( $this->log_dateformat ) 103 | . $spacer 104 | . trim( $message ) 105 | . $this->log_LFC 106 | ); 107 | fclose( $fp ) ; 108 | return true ; 109 | } 110 | function log(){ 111 | $mes = null; 112 | foreach($this->smtp_log as $line){ 113 | $mes .= trim( $line ) . $this->log_LFC; 114 | } 115 | $this->logWrite( null ,$mes ); 116 | $this->smtp_log = array(); 117 | } 118 | function logFilename( $data = null ){ 119 | if( is_null( $data ) ){ 120 | return $this->log_filename; 121 | } 122 | if( is_string( $data ) ){ 123 | $this->log_filename = $data; 124 | return $this->errorGather(); 125 | }else{ 126 | return $this->errorGather('Data specified error',__LINE__); 127 | } 128 | } 129 | function errorlogFilename( $data = null ){ 130 | if( is_null( $data ) ){ 131 | return $this->errorlog_filename; 132 | } 133 | if( is_string( $data ) ){ 134 | $this->errorlog_filename = $data; 135 | return $this->errorGather(); 136 | }else{ 137 | return $this->errorGather('Data specified error',__LINE__); 138 | } 139 | } 140 | } 141 | 142 | class QdsmtpBase extends QdsmtpError{ 143 | 144 | var $name = 'QdSmtp'; 145 | var $smtpLFC ="\r\n"; 146 | var $smtp_param = array( 147 | 'HOST' => null, 148 | 'FROM' => null, 149 | 'USER' => null, 150 | 'PASS' => null, 151 | 'PORT' => 25, 152 | 'PROTOCOL' => array( 153 | 'SMTP_AUTH','POP_BEFORE','SMTP' 154 | ), 155 | 'POP_SERVER'=> null, 156 | 'POP_USER' => null, 157 | 'POP_PASS' => null, 158 | 'CONTINUE' => null, 159 | ); 160 | var $protocol_def ='SMTP'; 161 | var $smtp_log = array(); 162 | var $smtp_auth_kind = array('PLAIN'); 163 | // var $smtp_auth_kind = array('CRAM-MD5','DIGEST-MD5','LOGIN','PLAIN'); 164 | var $data = null; 165 | var $continue = false; 166 | var $auto_kind = true; 167 | var $rcpt = array(); 168 | var $rcpt_stack = array(); 169 | var $rcpt_undone = array(); 170 | var $rcpt_undone_stack = array(); 171 | var $smtp_limit = 1000; 172 | var $sock = null; 173 | var $already_auth = false; 174 | // POP3 175 | var $pop3_time_file = 'qdsmtp.time'; 176 | var $pop3_use_file = true; 177 | var $pop3_connect_start = null; 178 | var $pop3_valid_minute = 10; 179 | var $time_out = 3 ; 180 | var $always_notify_success = false; 181 | 182 | function QdsmtpBase( $param = null ){ 183 | if( !is_null( $param[0] ) && is_bool( $param[0] ) ){ 184 | $this->continue = $continue; 185 | } 186 | if( is_array( $param[0] ) ){ 187 | $this->server( $param[0] ); 188 | } 189 | } 190 | 191 | //----------------------------------------- 192 | // User Interface except POP3 193 | //----------------------------------------- 194 | function server( $param = null ){ 195 | if( is_null( $param ) ){ 196 | return $this->smtp_param; 197 | }elseif( !is_array( $param ) ){ 198 | return $this->errorGather('Error Parameter type',__LINE__); 199 | } 200 | $param = array_change_key_case( $param , CASE_UPPER ); 201 | if( isset( $param['PROTOCOL'] ) && is_array($param['PROTOCOL'] ) ){ 202 | $param['PROTOCOL'] = array_change_key_case( $param['PROTOCOL'] , CASE_UPPER ); 203 | } 204 | if( isset( $param['ALWAYS_NOTIFY'] ) && is_bool($param['ALWAYS_NOTIFY']) ){ 205 | $this->always_notify_success = $param['ALWAYS_NOTIFY']; 206 | } 207 | $this->smtp_param = array_merge( $this->smtp_param , $param ); 208 | if( isset( $this->smtp_param['CONTINUE'] ) ){ 209 | $this->continue = $this->smtp_param['CONTINUE']; 210 | } 211 | return true; 212 | } 213 | function data( $data = null ){ 214 | if( is_null( $data ) ){ 215 | return $this->data; 216 | } 217 | if( is_string( $data ) ){ 218 | $this->data = $data; 219 | return $this->errorGather(); 220 | }else{ 221 | return $this->errorGather('Data specified error',__LINE__); 222 | } 223 | } 224 | function to( $recip , $add = false ){ 225 | return $this->recipient( $recip ); 226 | } 227 | function recipient( $recip , $add = false ){ 228 | if( !is_array( $recip ) ){ 229 | $recip = array( $recip ); 230 | } 231 | if( $add ){ 232 | $this->recipient = array_merge( $this->recipient , $recip ); 233 | $this->recipient = array_unique( $this->recipient ); 234 | }else{ 235 | $this->recipient = $recip; 236 | } 237 | return $this->errorGather(); 238 | } 239 | function addRecipient( $recip ){ 240 | return $this->recipient( $recip , true ); 241 | } 242 | function addto( $recip ){ 243 | return $this->addRecipient( $recip ); 244 | } 245 | function done( $stack = false ){ 246 | if( $stack ){ 247 | return $this->rcpt_stack; 248 | }else{ 249 | return $this->rcpt; 250 | } 251 | } 252 | function undone( $stack = false ){ 253 | if( $stack ){ 254 | return $this->rcpt_undone_stack; 255 | }else{ 256 | return $this->rcpt_undone; 257 | } 258 | } 259 | function continueConnect( $bool = null ){ 260 | if( is_null( $bool ) ){ 261 | return $this->continue; 262 | } 263 | if( is_bool( $bool ) ){ 264 | $this->continue = $bool; 265 | return $this->errorGather(); 266 | }else{ 267 | return $this->errorGather('Connection Continue Mode specifed error',__LINE__); 268 | } 269 | } 270 | function timeOut( $sec = null ){ 271 | if(is_null($sec)){ 272 | return $this->time_out; 273 | } 274 | if( is_numeric( $sec ) ){ 275 | $this->time_out = $sec; 276 | return $this->errorGather(); 277 | }else{ 278 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 279 | } 280 | } 281 | function errorlogLevel( $num = null ){ 282 | if( is_null( $num ) ){ 283 | return $this->errorlog_level; 284 | } 285 | if( is_numeric( $num ) ){ 286 | $this->errorlog_level = $num; 287 | return $this->errorGather(); 288 | }else{ 289 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 290 | } 291 | } 292 | function logLevel( $num = null ){ 293 | if( is_null( $num ) ){ 294 | return $this->log_level; 295 | } 296 | if( is_numeric( $num ) ){ 297 | $this->log_level = $num; 298 | return $this->errorGather(); 299 | }else{ 300 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 301 | } 302 | } 303 | function alwaysNotifySuccess( $bool = null ){ 304 | if(is_null($bool)){ 305 | return $this->always_notify_success; 306 | } 307 | if(is_bool($bool)){ 308 | $this->always_notify_success = $bool; 309 | return true; 310 | }else{ 311 | return false; 312 | } 313 | } 314 | //------------------------------------------ 315 | // Sending Method 316 | //------------------------------------------ 317 | // Qdsmtp ignore $option parameter 318 | function mail( $to , $subject , $message , $header= null , $option = null ){ 319 | $this->makeData( $to , $subject , $message , $header , $option ); 320 | return $this->send(); 321 | } 322 | 323 | function send( $data = null ){ 324 | 325 | if( !is_array($this->smtp_param['PROTOCOL'])){ 326 | $fg = $this->sendBase( $data , $this->smtp_param['PROTOCOL'] ); 327 | $this->log(); 328 | return $fg; 329 | } 330 | $stack = array( $this->error_display , $this->errorlog_level ); 331 | $this->error_display = false; 332 | $this->errorlog_level= 0; 333 | $ret = false; 334 | foreach($this->smtp_param['PROTOCOL'] as $protocol ){ 335 | if( $this->sendBase( $data , $protocol ) ){ 336 | $ret = true; 337 | $this->error_stack = array(); 338 | break; 339 | } 340 | } 341 | list( $this->error_display , $this->errorlog_level ) = $stack; 342 | if( !$ret ){ 343 | $fg = $this->errorGather( implode($this->smtpLFC , $this->error_stack) , __LINE__); 344 | $this->log(); 345 | return $fg; 346 | } 347 | if( !$this->continue ){ 348 | $this->close(); 349 | } 350 | $this->log(); 351 | return ( 0 === count( $this->error_stack ) ) && $this->errorGather(); 352 | } 353 | 354 | function close(){ 355 | $items = array( 356 | array( 'QUIT' , null ), 357 | ); 358 | list( $st , $mes , $com ) = $this->communicate( $items ); 359 | if( !$st ){ 360 | return $this->errorGather('Error at QUIT',__LINE__); 361 | } 362 | fclose( $this->sock ); 363 | } 364 | 365 | function sendBase( $data = null ,$protocol = null ){ 366 | if( !is_null( $data ) ){ 367 | $this->data = $data; 368 | } 369 | if( is_null( $protocol ) ){ 370 | $protocol = $this->protocol_def; 371 | } 372 | switch($protocol){ 373 | case 'POP_BEFORE'://POP3 374 | if( !$this->pop3() ){ 375 | return $this->errorGather('POP failure',__LINE); 376 | } 377 | case 'SMTP': 378 | if( !is_resource( $this->sock ) ){ 379 | $this->sock = $this->connect(); 380 | } 381 | $this->sayHello(); 382 | if( !$this->sendData() ){ 383 | return false; 384 | } 385 | break; 386 | case 'SMTP_AUTH': 387 | if(!is_resource($this->sock)){ 388 | $this->sock = $this->connect(); 389 | } 390 | 391 | if( !$this->already_auth ){ 392 | $this->sayHello(); 393 | if( 0 === preg_match('/[\s-]AUTH\s+([^\r\n]*)\r?\n/is', implode($this->smtpLFC,$this->smtp_log) , $matches)){ 394 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' doesnot suppoted SMTP AUTH Protocol',__LINE__); 395 | } 396 | $mes = strtoupper( $matches[1] ); 397 | $decide = null; 398 | foreach($this->smtp_auth_kind as $auth){ 399 | if( false !== strpos( $mes , $auth ) ){ 400 | $decide = $auth; 401 | break; 402 | } 403 | } 404 | if( is_null( $decide ) ){ 405 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' doesnot suppoted MY Abalable SMTP AUTH Protocol '.implode(' or ',$this->smtp_auth_kind),__LINE__); 406 | } 407 | $decide = strtolower( str_replace( '-' , '_' ,$decide ) ); 408 | if( !$this->{$decide}() ){ 409 | return $this->errorGather('Auth Error',__LINE__);; 410 | } 411 | $this->already_auth = true; 412 | } 413 | if( !$this->sendData() ){ 414 | $this->already_auth = false; 415 | return $this->errorGather('Send Data Error or Auth Error',__LINE__); 416 | } 417 | break; 418 | case 'OVER_SSL': 419 | break; 420 | default: 421 | break; 422 | } 423 | //debug 424 | /* 425 | if( 0==count($this->error_stack)){ 426 | $this->error_stack = 'no error'; 427 | } 428 | echo "
";
429 | 		echo htmlspecialchars(print_r($this->smtp_log , true));
430 | 		echo htmlspecialchars(print_r($this->error_stack , true));
431 | 		echo "
"; 432 | */ 433 | return $this->errorGather(); 434 | 435 | } 436 | 437 | 438 | //---------------------------------------------- 439 | // Comunication 440 | //---------------------------------------------- 441 | //------------------------------------------------------------------------- 442 | // see RFC821 443 | // in japanese ->http://www.sea-bird.org/doc/rfc_doc/rfc821-jp.txt(not me) 444 | //------------------------------------------------------------------------- 445 | var $smtp_status100 = array( 446 | 1 => 4, 447 | 2 => 3, 448 | 3 => 2, 449 | 4 => 1, 450 | 5 => 0, 451 | ); 452 | // 0 error 453 | // 1 continue 454 | // 2 final 455 | var $smtp_status10 = array( 456 | 0 => 0, 457 | 1 => 5, 458 | 2 => 1, 459 | 3 => 0, 460 | 4 => 0, 461 | 5 => 5, 462 | ); 463 | 464 | function sayHello(){ 465 | $items = array( 466 | array( 'EHLO' , $this->smtp_param['HOST'] ), 467 | array( 'HELO' , $this->smtp_param['HOST'] ), 468 | ); 469 | if( !$this->tryUntilSuccess( $items ) ){ 470 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' say no HELLO',__LINE__); 471 | } 472 | return $this->errorGather(); 473 | } 474 | function sendData(){ 475 | $reci = array(); 476 | 477 | if( '<' == substr( $this->smtp_param['FROM'],0,1) ){ 478 | $from = 'FROM:' . $this->smtp_param['FROM']; 479 | }else{ 480 | $from = 'FROM:<' . $this->smtp_param['FROM'] . '>'; 481 | } 482 | 483 | $items = array( 484 | array( 'MAIL' , $from ), 485 | ); 486 | list( $st , $mes , $com ) = $this->communicate($items); 487 | if( !$st ){ 488 | return $this->errorGather('Error From setting',__LINE__); 489 | } 490 | $this->rcpt = array(); 491 | $notify = $this->always_notify_success ? ' NOTIFY=SUCCESS,FAILURE':''; 492 | foreach( $this->recipient as $recipi ){ 493 | $items = array(array( 'RCPT' , 'TO:<' . $recipi . '>' . $notify )); 494 | list( $st , $mes , $com ) = $this->communicate($items); 495 | if( !$st ){ 496 | $this->rcpt_undone[] = $recipi ; 497 | $this->errorGather('Error RCPT setting',__LINE__); 498 | }else{ 499 | $this->rcpt[] = $recipi ; 500 | } 501 | } 502 | $this->rcpt_stack = array_merge( $this->rcpt_stack , $this->rcpt ); 503 | $this->rcpt_undone_stack = array_merge( $this->rcpt_undone_stack , $this->rcpt_undone ); 504 | $items = array(); 505 | $items[] = array( 'DATA' , null ); 506 | $items[] = array( 'DATA_CONTENT' , $this->smtpEscape($this->data).$this->smtpLFC . '.' ); 507 | $items[] = array( 'RSET' , null ); 508 | list( $st , $mes , $com ) = $this->communicate($items); 509 | if( !$st ){ 510 | return $this->errorGather('Error Data sending',__LINE__); 511 | } 512 | return $this->errorGather(); 513 | } 514 | 515 | 516 | function communicate( $items , $fp = null ){ 517 | if( is_null( $fp ) ){ 518 | $fp = $this->sock ; 519 | } 520 | $message = null ; 521 | if( !is_resource( $fp ) ){ 522 | return array( $this->errorGather( 'Error Resouce or stop connect' ,__LINE__) , $message , false ); 523 | } 524 | foreach( $items as $item ){ 525 | if( 'DATA_CONTENT' == $item[0] ){ 526 | $spacer = null ; 527 | $item[0] = null ; 528 | }elseif( 'DATA' == $item[0] || is_null($item[1]) ){ 529 | $spacer = null; 530 | }else{ 531 | $spacer = ' '; 532 | } 533 | $put_message = rtrim( $item[0] . $spacer . $item[1] ) . $this->smtpLFC; 534 | if( !fputs( $fp , $put_message ) ){ 535 | return array( $this->errorGather('SMTP can not fputs',__LINE__), $message , false ); 536 | } 537 | $this->smtp_log[] = $this->name . ' ' . $put_message ; 538 | 539 | do{ 540 | list( $st , $_message ) = $this->getMessage( $fp ); 541 | $message .= trim( $_message ) . $this->smtpLFC; 542 | if( !$st ){ 543 | return array( $this->errorGather('getMessage error',__LINE__), $message , $st ); 544 | } 545 | $branch = isset($this->smtp_status[$item[0]][$st]) ? $this->smtp_status[$item[0]][$st] : null; 546 | switch( $branch ){ 547 | case 'S': // Success 548 | $contine = false ; 549 | break; 550 | case 'F': // Failure 551 | return array( $this->errorGather('Failure :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 552 | break; 553 | case 'E': // Error 554 | return array( $this->errorGather('Error :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 555 | break; 556 | default: 557 | $s100 = (int) substr( $st , 0 , 1 ); 558 | $s10 = (int) substr( $st , 1 , 1 ); 559 | $s = $this->smtp_status100[$s100] * $this->smtp_status10[$s10]; 560 | switch($s){ 561 | case 0: // Error 562 | return array( $this->errorGather('Unkown Error :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 563 | break; 564 | case 3: //22X,220 565 | $contine = true ; 566 | break; 567 | case 10: //35X,354 568 | $contine = false ; 569 | break; 570 | case 15: //25X,250 Sucsess 571 | $contine = false ; 572 | break; 573 | default: 574 | $contine = false; 575 | break; 576 | } 577 | break; 578 | } 579 | }while($contine); 580 | } 581 | return array($this->errorGather() , $message , $st ); 582 | } 583 | 584 | function getMessage( $fp = null ){ 585 | if( is_null( $fp ) ){ 586 | $fp = $this->sock ; 587 | } 588 | if( !is_resource( $fp ) ){ 589 | return array( $this->errorGather( 'Error Resouce or stop connect' ,__LINE__) , null ); 590 | } 591 | $status = array(); 592 | $status[-1] = null; 593 | $message = array(); 594 | $count = 0; 595 | do{ 596 | $mes = fgets( $fp , 512 ); 597 | if( false === $mes ){ 598 | $er = stream_get_meta_data ( $fp ); 599 | $er_mes = null; 600 | if( true === $er ['timed_out'] ){ 601 | $er_mes = ' SYSTEM TIME OUT '; 602 | } 603 | return array( $this->errorGather('No Responce' . $er_mes,__LINE__) , null ); 604 | } 605 | $status[$count] = substr( $mes , 0 , 3 ); 606 | $_continue = substr( $mes , 3 , 1 ); 607 | $message[] = trim( $mes ); 608 | $this->smtp_log[] = 'Server '.$mes ; 609 | if( '-' == $_continue && ( ($status[$count] == $status[$count-1]) || (0 == $count)) ){ 610 | $continue = true ; 611 | }else{ 612 | $continue = false ; 613 | } 614 | $count ++; 615 | }while($continue); 616 | return array( $status[0] , implode( $this->smtpLFC , $message ) ); 617 | } 618 | 619 | function connect( $host = null , $port = null , $status = 220 ){ 620 | if(is_null($host)){ 621 | $host = $this->smtp_param['HOST']; 622 | } 623 | if(is_null($port)){ 624 | $port = $this->smtp_param['PORT']; 625 | } 626 | $sock = fsockopen( $host , $port , $err , $errst , $this->time_out ); 627 | if( !is_resource( $sock ) ){ 628 | return $this->errorGather('Connection error HOST: '.$host.' PORT: ' . $port ,__LINE__); 629 | } 630 | stream_set_timeout ( $sock , $this->time_out ); 631 | return $sock; 632 | } 633 | 634 | function tryUntilSuccess( $items ){ 635 | $try = false; 636 | $err_mes = array(); 637 | foreach( $items as $item ){ 638 | $err_mes[] = $item[0]; 639 | $this->error_ignore = true; 640 | list( $st , $mes , $com ) = $this->communicate( array( $item ) ); 641 | $this->error_ignore = false; 642 | if( true === $st ){ 643 | $try = true; 644 | $this->error = array(); 645 | break; 646 | } 647 | } 648 | if( !$try ){ 649 | return $this->errorGather( 'Tyr Error ->' . implode( ' ' , $err_mes ) ,__LINE__); 650 | } 651 | return $this->errorGather(); 652 | } 653 | 654 | //-------------------------- 655 | // AUTH 656 | //-------------------------- 657 | function plain(){ 658 | $plain = $this->makePlain(); 659 | $items = array(); 660 | foreach($plain as $pn ){ 661 | $items[] = array( 'AUTH PLAIN' , $pn ); 662 | } 663 | return $this->tryUntilSuccess( $items ); 664 | } 665 | function makePlain(){ 666 | $plain[0] = base64_encode($this->smtp_param['USER']."\0".$this->smtp_param['USER']."\0".$this->smtp_param['PASS']); 667 | $plain[1] = base64_encode($this->smtp_param['USER']."\0".$this->smtp_param['PASS']); 668 | return $plain; 669 | } 670 | //----------------------------------- 671 | // Utility 672 | //----------------------------------- 673 | function makeData( $to , $subject , $message , $header=null , $option=null ){ 674 | $recip = array(); 675 | $recip = array_merge( $recip , $this->extractAddr( $to ) ); 676 | $recip = array_merge( $recip , $this->extractAddr( $this->extractHeader('CC' , $header) ) ); 677 | $recip = array_merge( $recip , $this->extractAddr( $this->extractHeader('BCC' , $header) ) ); 678 | $this->recipient( $recip ); 679 | $head = trim( 'To: ' . $to . $this->smtpLFC . 'Subject: ' . trim( $subject ) . $this->smtpLFC . trim($header) ); 680 | return $this->data = $head . $this->smtpLFC . $this->smtpLFC . $message ; 681 | } 682 | 683 | function extractAddr( $line ){ 684 | if(0===preg_match_all('/,]+)>?\s*,?\s*/',$line,$matches)){ 685 | return array(); 686 | }else{ 687 | return $matches[1]; 688 | } 689 | } 690 | 691 | function extractHeader( $section , $header){ 692 | if( 0===preg_match('/'.$section.': (.*)\r?\n[^\s]/is' , $header , $matches ) ){ 693 | return null; 694 | }else{ 695 | return str_replace( array( "\r" , "\n" , "\t" , ' ' ) , null , $matches[1] ); 696 | } 697 | } 698 | 699 | function smtpEscape( $mes ){ 700 | $mes = preg_replace( '/\r?\n\.\r?\n/is' , $this->smtpLFC . '..' . $this->smtpLFC , $mes ); 701 | if( 0 !== preg_match( '/\r?\n[^\r\n]{'.$this->smtp_limit.',}\r?\n/is' , $mes ) ){ 702 | return $this->errorGather('SMTP Overfllow '.$this->smtp_limit.' chars in one line.',__LINE__); 703 | } 704 | return $mes; 705 | } 706 | //---------------------------------------------------- 707 | // POP3 708 | //---------------------------------------------------- 709 | function pop3(){ 710 | 711 | if( $this->pop3_use_file ){ 712 | if( !file_exists( $this->pop3_time_file ) ){ 713 | $this->writePop3Time(); 714 | } 715 | $this->pop3_connect_start = file_get_contents( $this->pop3_time_file ); 716 | }elseif( is_null( $this->pop3_connect_start ) ){ 717 | $this->pop3_connect_start = time(); 718 | } 719 | if( ( $this->pop3_connect_start + $this->pop3_valid_minute * 60 ) > time() ){ 720 | return $this->errorGather(); 721 | } 722 | 723 | if( $this->pop3_use_file ){ 724 | $this->writePop3Time(); 725 | } 726 | $fp = $this->connect( $this->smtp_param['POP_HOST'] , 110 , '+OK' ); 727 | if( false === $fp ){ 728 | return $this->errorGather('Can not connect POP3 Sever' ,__LINE__); 729 | } 730 | $items = array( 731 | array( 'USER' , $this->smtp_param['POP_USER'] ), 732 | array( 'PASS' , $this->smtp_param['POP_PASS'] ), 733 | ); 734 | $this->communicate( $items , $fp ); 735 | fclose( $fp ); 736 | return $this->errorGather(); 737 | } 738 | function pop3UseFile( $bool = null ){ 739 | if( is_null($bool)){ 740 | return $this->pop3_use_file; 741 | } 742 | if( is_bool( $bool ) ){ 743 | $this->pop3_use_file = $bool; 744 | return $this->errorGather(); 745 | }else{ 746 | return $this->errorGather('POP3 UseFile specifed error',__LINE__); 747 | } 748 | } 749 | function pop3TimeFilename( $filename = null ){ 750 | if(is_null($filename)){ 751 | return $this->pop3_time_file; 752 | } 753 | if( is_string( $filename ) && !empty( $filename ) ){ 754 | $this->pop3_time_file = $filename; 755 | return $this->errorGather(); 756 | }else{ 757 | return $this->errorGather('POP3 Filename specifed error',__LINE__); 758 | } 759 | } 760 | function pop3ValidMinute( $min = null ){ 761 | if(is_null($min)){ 762 | return $this->pop3_valid_minute; 763 | } 764 | if( is_numeric( $min ) ){ 765 | $this->pop3_valid_minute = $min; 766 | return $this->errorGather(); 767 | }else{ 768 | return $this->errorGather('POP3 Valid Minute specifed error',__LINE__); 769 | } 770 | } 771 | function writePop3Time(){ 772 | $fp_time = fopen($this->pop3_time_file,'w'); 773 | fputs( $fp_time , time() ); 774 | fclose($fp_time); 775 | } 776 | //-------------------------------- 777 | // Result Colleciton 778 | //-------------------------------- 779 | var $smtp_status= array( 780 | 781 | 'USER' => array( 782 | '+OK' => 'S', // for pop3 783 | ), 784 | 'PASS' => array( 785 | '+OK' => 'S', // for pop3 786 | ), 787 | 788 | 'HELO' => array( 789 | '250' => 'S', 790 | '500' => 'E', 791 | '501' => 'E', 792 | '504' => 'E', 793 | '421' => 'E', 794 | ), 795 | 'EHLO' => array( 796 | '250' => 'S', 797 | '500' => 'E', 798 | '501' => 'E', 799 | '504' => 'E', 800 | '421' => 'E', 801 | ), 802 | 'MAIL' => array( 803 | '250' => 'S', 804 | '500' => 'E', 805 | '501' => 'E', 806 | '421' => 'E', 807 | '552' => 'E', 808 | '451' => 'E', 809 | '452' => 'E', 810 | ), 811 | 'RCPT' => array( 812 | '250' => 'S', 813 | '251' => 'S', 814 | '550' => 'F', 815 | '551' => 'F', 816 | '552' => 'F', 817 | '553' => 'F', 818 | '450' => 'F', 819 | '451' => 'F', 820 | '452' => 'F', 821 | '500' => 'E', 822 | '501' => 'E', 823 | '503' => 'E', 824 | '421' => 'E', 825 | ), 826 | 'DATA' => array( 827 | '354' => 'S', 828 | '250' => 'S', 829 | '451' => 'F', 830 | '554' => 'F', 831 | '500' => 'E', 832 | '501' => 'E', 833 | '503' => 'E', 834 | '421' => 'E', 835 | ), 836 | 'AUTH PLAIN' => array( 837 | '235' => 'S', 838 | '503' => 'S', //503 5.5.0 Already Authenticated 839 | '501' => 'E', 840 | '535' => 'E', 841 | ), 842 | 'STARTTLS' => array( 843 | '220' => 'S', 844 | ), 845 | 'RSET' => array( 846 | '250' => 'S', 847 | ), 848 | 'QUIT' => array( 849 | '221' => 'S', 850 | '500' => 'E', 851 | '501' => 'E', 852 | '504' => 'E', 853 | ), 854 | ); 855 | } 856 | 857 | class Qdsmtp extends QdsmtpBase{ 858 | function Qdsmtp( $param = null ){ 859 | if( !is_null($param)){ 860 | $param = func_get_args(); 861 | } 862 | parent::QdsmtpBase( $param ); 863 | } 864 | } 865 | //------------------------------------------- 866 | // CakePHP Component 867 | //------------------------------------------- 868 | class QdsmtpComponent extends QdsmtpBase{ 869 | 870 | var $layout = 'default'; 871 | var $view_dir = 'email'; 872 | var $layout_dir = 'email'; 873 | var $template = 'default'; 874 | var $view = null; 875 | 876 | function QdsmtpComponent( $param = null ){ 877 | if( !is_null($param)){ 878 | $param = func_get_args(); 879 | } 880 | parent::QdsmtpBase( $param ); 881 | } 882 | 883 | function startup(&$controller) { 884 | $this->Controller =& $controller; 885 | if( defined( 'COMPONENTS' ) ){ 886 | $this->logFilename(COMPONENTS.$this->name.'.log'); 887 | $this->errorlogFilename( COMPONENTS . '_error' . $this->name . '.log' ); 888 | } 889 | return; 890 | } 891 | } 892 | ?> 893 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qdmail 2 | ====== 3 | 4 | Qdmail library series. 5 | Test has been run on PHP 5.2, 5.3, 5.4, 5.5, 5.6. 6 | 7 | [![Build Status](https://travis-ci.org/ftngrn/qdmail.png?tag=php-5)](https://travis-ci.org/ftngrn/qdmail) 8 | 9 | This series forked from http://hal456.net/ 10 | 11 | * [Qdmail](http://hal456.net/qdmail/) 12 | * [QdmailReceiver](http://hal456.net/qdmail_rec/) 13 | * [Qdsmtp](http://hal456.net/qdsmtp/) 14 | 15 | ## このライブラリについて ## 16 | Qdmailライブラリシリーズのgithub版です。 17 | 開発者のspokさんから以下のように許可を頂いています。 18 | ``` 19 | ライセンスに従って頂ければ何をしていただいても構いません。 20 | 多くの人に使われることが私の幸せです。 21 | ただ、本業が忙しく、qdmailを触る時間は、もうとれないだろうと思っております。 22 | できれば私の手を離れて誰かが管理してくれるとうれしいと思っています。 23 | ``` 24 | 25 | ## Associated repository ## 26 | * [Component for CakePHP2.x](https://github.com/spark-lab/Qdmail) by spark-lab 27 | 28 | ## License ## 29 | 30 | The MIT License (MIT) 31 | 32 | ## Copyright ## 33 | 34 | Qdmail 35 | Copyright (C)2008 spok 36 | http://hal456.net/qdmail_rec/ 37 | http://www.cpa-lab.com/tech/ 38 | -------------------------------------------------------------------------------- /Tests/QdmailReceiverTest.php: -------------------------------------------------------------------------------- 1 | 17 | To: SampleTo 18 | Subject: Sample subject 19 | Content-Type: text/plain; charset=ISO-2022-JP 20 | Content-Transfer-Encoding: 7bit 21 | 22 | Hello. 23 | This is sample mail for test. 24 | 25 | -- 26 | Signature 27 | _END_; 28 | $receiver = QdmailReceiver::start('direct', $mailsrc); 29 | $this->assertNotNull($receiver); 30 | $this->assertInstanceOf('QdDecodeDirect', $receiver); 31 | } 32 | } 33 | ?> 34 | -------------------------------------------------------------------------------- /Tests/QdmailTest.php: -------------------------------------------------------------------------------- 1 | qdm = new Qdmail(); 14 | $this->assertNotNull($this->qdm); 15 | $this->assertInstanceOf('Qdmail', $this->qdm); 16 | } 17 | } 18 | ?> 19 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ./Tests/ 7 | 8 | 9 | 10 | 11 | 12 | ./ 13 | 14 | ./Tests 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /qdmail_receiver.php: -------------------------------------------------------------------------------- 1 | {$property[0]}; 39 | } 40 | if( is_numeric( $func ) ){ 41 | $func = array( null , $func ); 42 | }elseif( is_string( $func ) ){ 43 | $func = array( $func , null ); 44 | } 45 | if( is_null($func[0]) ){ 46 | $func[0] = 'Unkwon Function'; 47 | }elseif( is_null($func[1]) ){ 48 | $func[1] = 'Unkwon Line Number'; 49 | } 50 | $fg = false; 51 | switch($type[0]){ 52 | case 'string': 53 | $fg = is_string( $param ); 54 | break; 55 | case 'bool': 56 | case 'boolean': 57 | $fg = is_bool( $param ); 58 | break; 59 | case 'numeric': 60 | if( isset( $type[1] ) ){ 61 | $fg = ( $type[1] < $param ) ; 62 | } 63 | if( isset( $type[2] ) ){ 64 | $fg = ( $type[2] > $param ) ; 65 | } 66 | break; 67 | default: 68 | break; 69 | } 70 | 71 | if($fg){ 72 | $this->{$property[0]} = $param; 73 | return true; 74 | }else{ 75 | return $this->error('Parameter Specified Error, Function name: ' . $func[0] , $func[1] ); 76 | } 77 | return false; 78 | } 79 | 80 | function arrayDigup( $array , $keys ){ 81 | $key = array_shift( $keys ); 82 | if( isset( $array[$key] ) ){ 83 | if( 0 === count( $keys ) ){ 84 | return $array[$key]; 85 | }else{ 86 | return $this->arrayDigup( $array[$key] , $keys ); 87 | } 88 | }elseif( isset( $array[0] )){ 89 | array_unshift( $keys , $key ); 90 | return $this->arrayDigup( $array[0] , $keys ); 91 | }else{ 92 | return false; 93 | } 94 | } 95 | 96 | } 97 | //------------------------------------------------------------ 98 | // QdmailReceiverDebug 99 | //------------------------------------------------------------ 100 | class QdmailReceiverDebug extends QdmailReceiverFunc{ 101 | 102 | var $debug = 0; //degub level 103 | var $debug_echo_charset = 'utf-8'; 104 | var $LFC = "\r\n"; 105 | var $log_LFC = "\r\n"; 106 | //------------------------------- 107 | // Debug 108 | //------------------------------- 109 | function debug( $level=null ){ 110 | if( is_null( $level ) || !is_numeric($level) ){ 111 | return $this->debug; 112 | } 113 | $this->debug = $level ; 114 | return true; 115 | } 116 | function debugEchoLine(){ 117 | $vars = func_get_args(); 118 | $this->debugEcho( false , $vars ); 119 | } 120 | function debugEchoLf(){ 121 | $vars = func_get_args(); 122 | $this->debugEcho( true , $vars ); 123 | } 124 | function debugEcho( $lf , $vars = null ){ 125 | 126 | static $already_header = false; 127 | static $already_footer = false; 128 | if( 1 > $this->debug ){ 129 | return; 130 | } 131 | if( !$already_header ){ 132 | $head=''; 133 | echo $head ; 134 | $already_header = true ; 135 | } 136 | if( $already_header && ( 'END' === $lf ) && !$already_footer){ 137 | $foot =''; 138 | echo $foot; 139 | $already_footer = true; 140 | return ; 141 | } 142 | $out = null; 143 | if( !is_array( $vars ) ){ 144 | $vars =array( $vars ); 145 | } 146 | foreach($vars as $var){ 147 | $_out = print_r( $var , true ) ; 148 | $enc = mb_detect_encoding( $_out ); 149 | if( strtoupper( $this->debug_echo_charset ) !== strtoupper( $enc ) ){ 150 | $_out = mb_convert_encoding( $_out , $this->debug_echo_charset , $enc ); 151 | } 152 | $out .= $_out . $this->LFC; 153 | } 154 | $spacer = $this->log_LFC ; 155 | if( !$lf ){ 156 | $out = preg_replace("/\\r?\\n/",' ',$out); 157 | $spacer = null ; 158 | } 159 | 160 | echo "
";
 161 | 		$out = htmlspecialchars( $this->name . ' Debug: ' . $spacer . trim( $out ) , ENT_QUOTES , $this->debug_echo_charset );
 162 | 		echo  $out;
 163 | 		echo "
"; 164 | } 165 | } 166 | //---------------------------------------------------------------------------- 167 | // QdmailReceiverError 168 | //---------------------------------------------------------------------------- 169 | class QdmailReceiverError extends QdmailReceiverDebug{ 170 | 171 | var $error =array(); 172 | var $noteice =array(); 173 | var $error_display = true; 174 | var $error_fatal_ignore = false; 175 | 176 | function error( $message = null, $line = null , $fatal = false){ 177 | if( is_null( $message ) ){ 178 | return $this->error; 179 | } 180 | if(!is_null($line)){ 181 | $message .= ' line: ' . $line; 182 | } 183 | if($this->error_display){ 184 | echo '
'.get_class($this).': '.$message; 185 | } 186 | $this->error[] = $message; 187 | if( $fatal && !$this->error_fatal_ignore ){ 188 | die( "\r\n".get_class($this) . ' Fatal Error line: ' .__LINE__); 189 | } 190 | return false; 191 | } 192 | function errorFatal( $message , $line = null , $fatal = false ){ 193 | return $this->error( $message , $line , !$this->error_fatal_ignore ); 194 | } 195 | function errorDisplay( $param = null ){ 196 | return $this->option($param,array('errorDisplay',__LINE__),array('bool'),array('error_display')); 197 | } 198 | function errorFatalIgnore( $param = null ){ 199 | return $this->option($param,array('errorFatalIgnore',__LINE__),array('bool'),array('error_fatal_ignore')); 200 | } 201 | } 202 | //----------------------------------------------------------------------------- 203 | // QdDecodeBase 204 | //----------------------------------------------------------------------------- 205 | class QdDecodeBase extends QdmailReceiverError{ 206 | 207 | var $name = 'QdDecodeBase'; 208 | var $version = '0.1.4.alpha'; 209 | var $x_licese = 'The_MIT_License'; 210 | var $x_url = 'http://hal456.net/qdmail_rec/'; 211 | 212 | var $target_charset = null; 213 | var $header_all = null; 214 | var $headr = array(); 215 | var $header_name = array(); 216 | var $body_all = null; 217 | var $all = null; 218 | var $body = array(); 219 | var $attach = array(); 220 | var $done = false; 221 | var $line = array(); 222 | var $num = 0; 223 | var $max = 0; 224 | var $text_decode = true;// including html 225 | var $is_html = null; 226 | var $attach_decode = true; 227 | var $already_header = false; 228 | var $already_text = false;// including html 229 | var $already_attach = false; 230 | var $already_getMail= false; 231 | var $other_multipart = array( 232 | 'alternative' =>'skip', 233 | 'related' =>'skip', 234 | 'signed' =>'skip', 235 | 'mixed' =>'skip', 236 | 'x-mixed-replace' =>'skip', 237 | 'parallel' =>'skip', 238 | 'encrypted' =>'skip', 239 | ); 240 | 241 | //-------------- 242 | // Constructor 243 | //-------------- 244 | function __construct( $param = null ){ 245 | $this->QdDecodeBase( $param ); 246 | } 247 | function QdDecodeBase( $param = null ){ 248 | 249 | $enc = mb_internal_encoding(); 250 | if( !empty( $enc ) ){ 251 | $this->charset( $enc ); 252 | } 253 | 254 | if( isset( $param[1] ) && is_string( $param[1] )){ 255 | $this->target_charset = $param[1]; 256 | } 257 | if( isset( $param['name'] ) ){ 258 | $this->name = $param['name']; 259 | } 260 | 261 | } 262 | 263 | //*********************************************************** 264 | //** For OverRide ** 265 | //*********************************************************** 266 | function getMail(){ 267 | $this->_before_getMail(); 268 | $this->_after_getMail(); 269 | } 270 | function buildHeader( $header_laof ){ 271 | return $this->_before_buildHeader( $header_laof ); 272 | } 273 | //------------------------------- 274 | // Get(Set) One Mail 275 | //------------------------------- 276 | function _before_getMail(){ 277 | $this->alreadyReset(); 278 | } 279 | function _after_getMail(){ 280 | $this -> already_getMail = true; 281 | } 282 | //----------------------------------------------------------------------- 283 | // Option Specify 284 | //$this->option( 285 | // $param, // parameter 286 | // array('charset',__LINE__), // MyFunction Name & line for ErrorCode 287 | // array('string'), // Type , 'false' means ReadOnly 288 | // array('target_charset') // My propatey_name for change 289 | // ); 290 | //----------------------------------------------------------------------- 291 | function charset( $param = null ){ 292 | return $this->option($param,array('charset',__LINE__),array('string'),array('target_charset')); 293 | } 294 | function headerAll( $param = null ){ 295 | return $this->option($param,array('headerAll',__LINE__),false,array('header')); 296 | } 297 | // all eq set 298 | function all( $param = null ){ 299 | $ret = $this->option($param,array('all',__LINE__),array('string'),array('all')); 300 | if( !is_null( $param ) && false!==$ret && !empty( $ret ) ){ 301 | $name = isset( $this->name ) ? $this->name : get_class( $this ); 302 | $ret = $this->all = 'X-' . $name . ': ' . 'version'.$this->version .'-'. $this->x_licese . ' ' . $this->x_url . "\r\n" . ltrim( $this->all ) ; 303 | } 304 | return $ret; 305 | } 306 | function set( $param = null ){ 307 | return $this->all( $param ); 308 | } 309 | function bodyDecode( $param = null ){ 310 | return $this->option($param,array('textDecode',__LINE__),array('bool'),array('text_decode')); 311 | } 312 | function attachDecode( $param = null ){ 313 | return $this->option($param,array('textDecode',__LINE__),array('bool'),array('attach_decode')); 314 | } 315 | function header( $param = null , $return_false = false ){ 316 | 317 | if( !$this->already_header ){ 318 | $this->decodeHeader(); 319 | } 320 | if(is_null($param)){ 321 | return $this->header; 322 | } 323 | if( is_string($param) && ( 'all' === strtolower($param) )){ 324 | return $this->headerAll(); 325 | } 326 | if(is_string($param)){ 327 | $param = array( $param ); 328 | } 329 | $ret = $this->arrayDigup( $this->header , $param ) ; 330 | return ( false === $ret ) ? $return_false:$ret; 331 | } 332 | function headerName( $param = null ){ 333 | return $this->option($param,array('headerName',__LINE__) , false , array('header_name')); 334 | } 335 | function bodyFull( $param = null ){ 336 | if( !$this->already_text || !$this->already_attach ){ 337 | $this->decodeBody(); 338 | } 339 | return array_merge( $this->body() , array( 'attach' => $this->attach( $param = null ) ) ); 340 | } 341 | function body( $param = null ){ 342 | if( !$this->already_text ){ 343 | $this->decodeBody(); 344 | } 345 | if( !is_array( $param ) ){ 346 | $param = array( $param ); 347 | } 348 | $ret = $this->option(null,array( 'body' ,__LINE__) , false , array('body') ); 349 | $ret = $this->arrayDigup( $ret , $param ) ; 350 | return $ret; 351 | } 352 | function bodyAutoSelect(){ 353 | $ret = $this->body(array('html','value')); 354 | if( !empty( $ret ) ){ 355 | $this->is_html = true; 356 | return $ret; 357 | } 358 | $ret = $this->body(array('text','value')); 359 | if( !empty( $ret ) ){ 360 | $this->is_html = false; 361 | return $ret; 362 | } 363 | return false; 364 | } 365 | function isHtml(){ 366 | if( !isset( $this->is_html ) ){ 367 | if( false === $this->bodyAutoSelect() ){ 368 | return false; 369 | } 370 | } 371 | return $this->option( null ,array('isHtml',__LINE__),false,array('is_html')); 372 | } 373 | 374 | function text(){ 375 | return $this->body(array('text','value')); 376 | } 377 | function html(){ 378 | return $this->body(array('html','value')); 379 | } 380 | function attach( $param = null ){ 381 | if( !$this->already_attach ){ 382 | $this->decodeBody(); 383 | } 384 | return $this->option($param,array('attach',__LINE__) , false , array('attach') ); 385 | } 386 | function version(){ 387 | return $this->version; 388 | } 389 | //--------------------------------------- 390 | // Parameter settings & analysis 391 | //--------------------------------------- 392 | function alreadyReset(){ 393 | $this->already_header = false; 394 | $this->already_text = false; 395 | $this->already_attach = false; 396 | $this -> already_getMail = false; 397 | $this->header_all = null; 398 | $this->body_all = null; 399 | $this->header = array(); 400 | $this->header_name = array(); 401 | $this->body = array(); 402 | $this->attach =array(); 403 | return true; 404 | } 405 | function decodeHeader(){ 406 | static $addr = array( 407 | 'to', 408 | 'cc', 409 | 'bcc', 410 | 'reply-to', 411 | 'from', 412 | ); 413 | 414 | if( !$this -> already_getMail ){ 415 | $this->getMail(); 416 | } 417 | 418 | // cutting 419 | if( 0 === preg_match( '/\r?\n\r?\n/is', trim( $this->all ), $matches, PREG_OFFSET_CAPTURE)){ 420 | $this->header_all = $this->all ; 421 | $this->body_all = null; 422 | }else{ 423 | $offset = $matches[0][1] ; 424 | $this->header_all = trim( substr( $this->all , 0 , $offset ) ) ; 425 | $this->body_all = trim( substr( $this->all , $offset + 1 ) ) ; 426 | } 427 | $this->header = $this->buildHeader( $this->header_all ); 428 | // address field action , force to array type 429 | foreach( $addr as $ad ){ 430 | if( !isset( $this->header[$ad]) ){ continue; } 431 | if(is_array($this->header[$ad])){ 432 | $addr_header = array_shift( $this->header[$ad] ); 433 | }else{ 434 | $addr_header = $this->header[$ad]; 435 | } 436 | $person = explode( ',' , $addr_header ); 437 | $this->header[$ad] = array(); 438 | foreach($person as $pers){ 439 | if( !empty( $pers ) ){ 440 | $this->header[$ad][] = $this->splitMime( $pers , true ); 441 | } 442 | } 443 | } 444 | // subject 445 | if( isset( $this->header['subject'] ) ){ 446 | $this->header['subject']= $this->splitMime( $this->header['subject'] , false ); 447 | } 448 | $this->already_header = true; 449 | } 450 | 451 | function decodeBody(){ 452 | if( !$this->already_header ){ 453 | $this->decodeHeader(); 454 | } 455 | // body 456 | if( ( !$this->already_text && $this->text_decode ) 457 | || 458 | ( !$this->already_attach && $this->attach_decode ) ){ 459 | if( isset( $this->header['content-type'] ) ){ 460 | $type = $this->typeJudge( $this->header['content-type'] ); 461 | preg_match( '/boundary\s*=\s*"([^"]+)"/is' , $this->header['content-type'] , $matches ); 462 | if( isset( $matches[1] ) ){ 463 | $this->line = preg_split( '/\r?\n/is' , $this->body_all ); 464 | $this->num = 0; 465 | $this->max = count( $this->line ); 466 | $this->buildPart( $matches[1] , $type ); 467 | }else{ 468 | $this->body[$type] = $this->makeBody( $this->header , $this->body_all ); 469 | $this->already_text = true; 470 | } 471 | }else{ 472 | $type ='unknown'; 473 | $_hd = array( 'content-type' => $type . '/' . $type ); 474 | $this->body[$type] = $this->makeBody( array_merge( $this->header , $_hd ) , $this->body_all ); 475 | } 476 | } 477 | } 478 | //---------------------------- 479 | // Header Build And Make 480 | //---------------------------- 481 | function _before_buildHeader( $header_laof ){ 482 | $header = array(); 483 | $line = preg_split( '/\r?\n/is' , trim( $header_laof ) ); 484 | // connect line 485 | $prev_key = 0; 486 | foreach($line as $key => $li){ 487 | if(1 === preg_match('/^\s/',$li)){ 488 | $line[$key] = $line[$prev_key] ."\r\n". $line[$key]; 489 | unset( $line[$prev_key] ); 490 | } 491 | $prev_key = $key; 492 | } 493 | // split header 494 | foreach($line as $li){ 495 | if( false !== ( $split = strpos( $li , ':' ) ) ){ 496 | $obj = trim( substr( $li , $split+1 ) ); 497 | $p_name = strtolower( $org = substr( $li , 0 , $split ) ); 498 | if( isset($header[$p_name]) && !is_array($header[$p_name]) ){ 499 | $temp = $header[$p_name]; 500 | $header[$p_name] = array( $temp , $obj ) ; 501 | }elseif( isset($header[$p_name]) && is_array($header[$p_name]) ){ 502 | $header[$p_name][] = $obj; 503 | }else{ 504 | $header[$p_name] = $obj; 505 | $this->header_name[$p_name] = $org ; 506 | } 507 | }else{ 508 | continue; 509 | } 510 | } 511 | return $header; 512 | } 513 | //-------------------------------------- 514 | // Multipart 515 | //-------------------------------------- 516 | function buildPart( $boundary , $type){ 517 | $body = array(); 518 | if( !$this->skipto( '--' . $boundary ) ){ 519 | return false; 520 | } 521 | do{ 522 | // header in body 523 | $header = null; 524 | do{ 525 | $li = $this->get_1_line( false ); 526 | if( false === $li ){ 527 | return false; 528 | } 529 | $header .= $li . "\r\n" ; 530 | }while( !empty( $li ) || '0' === $li ); 531 | 532 | $header = $this->buildHeader( $header ); 533 | 534 | if( isset( $header['content-type'] ) ){ 535 | $type = $this->typeJudge( $header['content-type'] ); 536 | preg_match( '/boundary\s*=\s*"([^"]+)"/is' , $header['content-type'] , $matches ); 537 | if( !empty( $matches[1] ) ){ 538 | $this->buildPart( $matches[1] , $type ); 539 | } 540 | }else{ 541 | $type = 'unknown'; 542 | $header['content-type'] = $type . '/' . $type; 543 | } 544 | // ||< 545 | if( ( !$this->attach_decode && ( $type == 'attach' || $type == 'unknown' ) ) 546 | || 547 | ( !$this->text_decode && ( $type == 'text' || $type == 'html' ) ) 548 | ){ 549 | 550 | if( !$this->skipto('--'.$boundary) ){ 551 | return false; 552 | } 553 | continue ; 554 | } 555 | 556 | $plain_body = null; 557 | $li = $this->get_1_line(false); 558 | while( ( trim( $li ) != '--'.$boundary.'--') && ( trim($li) != '--'.$boundary) && (false !== $li ) ){ 559 | $plain_body .= $li . "\r\n" ; 560 | $li = $this->get_1_line( false ); 561 | } 562 | $_body = $this->makeBody( $header , $plain_body ); 563 | if( $_body['attach_flag'] ){ 564 | $type = 'attach'; 565 | } 566 | 567 | if( $type == 'attach' || $type == 'unknown'){ 568 | $this->attach[] = $_body ; 569 | $this->already_attach = true ; 570 | }elseif( $type == 'text' || $type == 'html'){ 571 | $this->body[$type] = $_body ; 572 | $this->already_text = true ; 573 | }//else $type === 'skip' 574 | 575 | }while( trim( $li ) == '--'.$boundary ); 576 | return true; 577 | } 578 | //---------------------------------------- 579 | // Decode MimePart and set to Result Array 580 | //---------------------------------------- 581 | function makeBody( $header , $body ){ 582 | 583 | if(1===preg_match('/charset\s*=\s*"?([^\s;"]+)"?\s*;?\r?\n?/is',$header['content-type'],$matches)){ 584 | $charset = $matches[1]; 585 | } 586 | 587 | $encoding = isset($header['content-transfer-encoding']) ? $header['content-transfer-encoding'] : '7bit' ; 588 | 589 | if( !is_null( $body ) && ( 'base64' === strtolower( $encoding ) ) ){ 590 | $body = base64_decode( $body ); 591 | }elseif( !is_null( $body ) && ( 'quoted-printable' === strtolower( $encoding ) ) ){ 592 | $body = quoted_printable_decode( $body ); 593 | } 594 | 595 | if( !is_null( $body ) && ( 1===preg_match('/text\//is' , $header['content-type'] , $matches ) ) ){ 596 | $charset = isset($charset) ? $charset : mb_detect_encoding( $body ); 597 | 598 | // mb_check_encoding ([ string $var [, string $encoding ]] ) 599 | if( false !== strpos( strtoupper( $charset ) , 'UNKNOWN' ) ){ 600 | $charset = mb_detect_encoding($body); 601 | } 602 | $stack = mb_detect_order(); 603 | if( ( false !== mb_detect_order( $charset ) ) && isset( $this->target_charset ) && ( strtolower($this->target_charset) != strtolower($charset))){ 604 | if( mb_check_encoding( $body , $charset ) ){ 605 | $body = mb_convert_encoding( $body , $this->target_charset , $charset ); 606 | } 607 | } 608 | mb_detect_order($stack); 609 | } 610 | 611 | $ret = array(); 612 | if( isset( $charset ) ){ 613 | $ret['charset'] = $charset; 614 | } 615 | // attachment or other 616 | $ret['attach_flag'] = false; 617 | if( !empty( $header['content-disposition'] ) || !empty( $header['content-id'] ) ){ 618 | $ret['attach_flag'] = true; 619 | } 620 | if( !empty( $header['content-id'] ) ){ 621 | $ret['content-id'] = $header['content-id']; 622 | $ret['content-id_essence'] = trim(trim($header['content-id'],'<>')); 623 | } 624 | 625 | // filename 626 | $filename = ''; 627 | if( $ret['attach_flag'] && (1===preg_match('/name\s*=\s*"?([^"\s\r\n]+)"?\r?\n?/is',$header['content-type'] , $matches ) ) ){ 628 | $filename = $matches[1]; 629 | }elseif($ret['attach_flag'] && isset($header['content-disposition']) && (1===preg_match('/name\s*=\s*"?([^"\s\r\n]+)"?\r?\n?/is' , $header['content-disposition'] , $matches ) ) ){ 630 | $filename = $matches[1]; 631 | } 632 | if( 1 === preg_match( '/(=\?.+\?=)/is ' , $filename , $matches ) ){ 633 | $_filename = $this->qd_decode_mime($matches[1]); 634 | $org_charset = mb_internal_encoding(); 635 | if( isset( $this->target_charset ) && ( strtolower($this->target_charset) != strtolower($org_charset))){ 636 | if( mb_check_encoding( $_filename , $org_charset ) ){ 637 | $_filename = mb_convert_encoding( 638 | $_filename , 639 | $this->target_charset, 640 | $org_charset 641 | ); 642 | } 643 | } 644 | 645 | $filename = str_replace( $matches[1] , $_filename , $filename ); 646 | } 647 | 648 | $filename = trim($filename); 649 | if( !empty( $filename ) ){ 650 | $ret['filename'] = $filename; 651 | $ret['filename_safe'] = urlencode($filename); 652 | } 653 | 654 | //mimetype 655 | if( 1 === preg_match('/^\s*([^\s]*\/[^\s;]+)/is' , $header['content-type'] , $matches ) ){ 656 | $ret['mimetype'] = $matches[1] ; 657 | } 658 | 659 | $ret['enc'] = $encoding; 660 | $ret['content-type'] = $header['content-type']; 661 | $ret['value'] = $body; 662 | return $ret; 663 | } 664 | //------------------- 665 | // Mimetype Judge 666 | //------------------- 667 | function typeJudge( $value ){ 668 | preg_match('/\s*([^\s;,]+\/[^\s;,]+)\s*;?/is',$value,$matches); 669 | if( !empty( $matches[1] ) && 'TEXT/PLAIN'==strtoupper($matches[1])){ 670 | $type = 'text'; 671 | }elseif( !empty( $matches[1] ) && 'TEXT/HTML'==strtoupper($matches[1])){ 672 | $type = 'html'; 673 | }elseif( !empty( $matches[1] ) ){ 674 | $slash = strpos( $matches[1] , '/' ); 675 | if( false !==$slash ){ 676 | $mime_main = strtolower( substr( $matches[1] , 0 , $slash ) ); 677 | $mime_sub = strtolower( substr( $matches[1] , $slash+1 ) ); 678 | if( 'multipart' === $mime_main ){ 679 | if(isset($this->other_multipart[$mime_sub]) && 'skip'===$this->other_multipart[$mime_sub]){ 680 | $type = 'skip'; 681 | }else{ 682 | $type = 'unknown'; 683 | } 684 | }else{ 685 | $type = 'attach'; 686 | } 687 | }else{ 688 | $type = 'attach'; 689 | } 690 | }else{ 691 | $type = 'attach'; 692 | } 693 | return $type; 694 | } 695 | //--------------------- 696 | // Step 697 | //--------------------- 698 | function skipto( $boundary ){ 699 | $fg = true; 700 | while( trim( $this->line[$this->num] ) != trim( $boundary ) ){ 701 | $this->num++; 702 | if( $this->num >= $this->max ){ 703 | $fg = false; 704 | break; 705 | } 706 | } 707 | return $fg; 708 | } 709 | 710 | function get_1_line( $empty = true ){ 711 | $fg = true; 712 | do{ 713 | if( !isset( $this->line[$this->num] ) ){ 714 | $fg = false; 715 | break; 716 | } 717 | $li = rtrim( $this->line[$this->num++] ); 718 | if( $this->num >= $this->max ){ 719 | $fg = false; 720 | break; 721 | } 722 | }while( $empty && empty($li) ); 723 | return $fg ? $li:false; 724 | } 725 | 726 | //------------------ 727 | // Mime Decode 728 | //------------------ 729 | function qd_decode_mime( $string ){ 730 | $ret = mb_decode_mimeheader($string); 731 | return $ret; 732 | } 733 | function splitMime( $var , $address_mode = false){ 734 | $obj = array(); 735 | $obj['value'] = trim( $var ); 736 | 737 | $var = preg_replace( '/\r?\n\s*=\?/' ,'=?',$var ); 738 | preg_match_all('/=\?(?:(?!\?=).)*\?=/is' , $var , $matches); 739 | 740 | $mime_fg =false; 741 | if( 0 < count($matches[0]) ){ 742 | $rep = array(); 743 | foreach($matches[0] as $one){ 744 | $rep[] = $this->qd_decode_mime( $one ); 745 | } 746 | $var = str_replace($matches[0],$rep,$var); 747 | $mime_fg = true; 748 | } 749 | 750 | if( $address_mode && ( 1 === preg_match( '/\s]+@[^<>\s]+\.[^<>\s]+)>?/is' , $var , $matches ) ) ){ 751 | $obj['mail'] = trim( $matches[1] ); 752 | $obj['name'] = trim( trim( str_replace( $matches[0] , '' , $var ),"\" \t") ); 753 | if(empty($obj['name'])){ 754 | unset($obj['name']); 755 | } 756 | }elseif(!$address_mode){ 757 | $obj['name'] = $var; 758 | } 759 | 760 | if( $mime_fg && !empty($obj['name']) ){ 761 | $org_charset = mb_internal_encoding(); 762 | $org_charset = empty($org_charset) ? null:$org_charset; 763 | if( isset( $this->target_charset ) && ( strtolower($this->target_charset) != strtolower($org_charset))){ 764 | $obj['name'] = trim(mb_convert_encoding( 765 | $obj['name'], 766 | $this->target_charset, 767 | $org_charset 768 | )); 769 | } 770 | } 771 | return $obj; 772 | } 773 | } 774 | //------------------------- 775 | // Normal Qddecode 776 | //------------------------- 777 | class QdDecode extends QdDecodeBase{ 778 | 779 | var $name ='QdDecode'; 780 | 781 | function QdDecode( $param = null ){ 782 | if( !is_null( $param ) ){ 783 | $param = func_get_args(); 784 | } 785 | parent::__construct( $param ); 786 | } 787 | } 788 | 789 | /** 790 | * QdPop ver 0.1.4a 791 | * POP Receiver for PHP 792 | * 793 | * PHP versions 4 and 5 (PHP4.3 upper) 794 | * 795 | * Copyright 2008, Spok in japan , tokyo 796 | * hal456.net/qdmail_decode : http://hal456.net/qdmail_rec/ 797 | * & CPA-LAB/Technical : http://www.cpa-lab.com/tech/ 798 | * Licensed under The MIT License License 799 | * 800 | * @copyright Copyright 2008, Spok. 801 | * @link http://hal456.net/qdmail_rec/ 802 | * @version 0.1.4alafa 803 | * @lastmodified 2008-09-15 804 | * @license The MIT License http://www.opensource.org/licenses/mit-license.php 805 | * 806 | * Qdmail is sending e-mail library for multibyte language , 807 | * easy , quickly , usefull , and you can specify deeply the details. 808 | * Copyright (C) 2008 spok 809 | */ 810 | 811 | class QdPopBase extends QdDecodeBase{ 812 | 813 | var $name = 'QdPopBase'; 814 | 815 | var $server = array( 816 | 'host'=>'', 817 | 'port'=>110, 818 | 'user'=>'', 819 | 'pass'=>'', 820 | ); 821 | var $time_out = 5; 822 | var $pointer = 1; 823 | var $count = 0; 824 | var $delete = false; 825 | var $fp = null; 826 | var $pop_high_speed = -1; 827 | var $get_uid = true; 828 | var $popuid = 'popuid'; 829 | var $uid_list = array(); 830 | 831 | //-------------------------- 832 | // Constructor 833 | //-------------------------- 834 | function __construct( $param = null ){ 835 | $this->QdPopBase($param); 836 | } 837 | function QdPopBase( $server ){ 838 | if( isset( $server[0] ) ){ 839 | $this->server = array_merge( $this->server , $server[0] ); 840 | } 841 | parent::__construct( $server ); 842 | } 843 | 844 | function buildHeader( $header_laof ){ 845 | 846 | $header = $this->_before_buildHeader( $header_laof ); 847 | 848 | if( $this->get_uid ){ 849 | $i = 0; 850 | $key = $this->popuid; 851 | while( isset($header[$key]) && $i < 10000 ){ 852 | $key = $this->popuid.'_'.$i++; 853 | } 854 | $id = $this->getUid() ; 855 | if( false !== $id ){ 856 | $header[$key] = $id; 857 | } 858 | } 859 | return $header; 860 | } 861 | 862 | //----------------------------------------------------------------------- 863 | // Option Specify 864 | //$this->option( 865 | // $param, // parameter 866 | // array('charset',__LINE__), // MyFunction Name & line for ErrorCode 867 | // array('string'), // Type , 'false' means ReadOnly 868 | // array('target_charset') // My propatey_name for change 869 | // ); 870 | //----------------------------------------------------------------------- 871 | function popHigh( $param = null ){ 872 | return $this->option($param,array('popHigh',__LINE__),array('numeric',-1),array('pop_high_speed')); 873 | } 874 | function popUid( $param = null ){ 875 | return $this->option($param,array('popUid',__LINE__),array('bool'),array('get_uid')); 876 | } 877 | function popUidAll(){ 878 | if( empty( $this->uid_list ) ){ 879 | $fg = $this->getUidAll(); 880 | if( !$fg ){ 881 | return false; 882 | } 883 | } 884 | return $this->option( null ,array('popUidAll',__LINE__),false,array('uid_list')); 885 | } 886 | function timeout( $param = null ){ 887 | $fg = $this->option($param,array('timeout',__LINE__),array('numeric',0),array('time_out')); 888 | if( is_resource( $this->fp ) && is_numeric( $this->time_out ) ){ 889 | stream_set_timeout ( $this->fp , $this->time_out ) ; 890 | } 891 | return $fg; 892 | } 893 | function deleteAlways( $param = null ){ 894 | return $this->option($param,array('deleteAlways',__LINE__),array('bool'),array('delete')); 895 | } 896 | //---------------------------------- 897 | // make Connection and close 898 | //---------------------------------- 899 | function connect(){ 900 | $this->fp=fsockopen($this->server['host'],$this->server['port'], $err , $errst , $this->time_out ); 901 | if(!is_resource($this->fp)){ 902 | return $this->errorFatal('Connection Failure \''.$this->server['host'].'\' Port \''.$this->server['port'].'\'',__LINE__); 903 | } 904 | stream_set_timeout ( $this->fp , $this->time_out ); 905 | $this->getMessage( true ); 906 | list($fg1,$void)=$this->communicate('USER '.$this->server['user'],array('USER ID Error',__LINE__,!$this->error_fatal_ignore),true); 907 | list($fg2,$void)=$this->communicate('PASS '.$this->server['pass'],array('PASS ID Error',__LINE__,!$this->error_fatal_ignore),true); 908 | 909 | $this->uid_list =array(); 910 | return $fg1 && $fg2 ; 911 | } 912 | function close(){ 913 | $_ret = $this->cmd('QUIT',null,true); 914 | return fclose($this->fp) && $_ret; 915 | } 916 | function done(){ 917 | return $this->close(); 918 | } 919 | 920 | //---------------------------------- 921 | // mail operation 922 | //---------------------------------- 923 | function preCheck(){ 924 | if(!is_resource($this->fp)){ 925 | return $this->connect(); 926 | } 927 | return true; 928 | } 929 | function count( $retry = false ){ 930 | 931 | if(!$retry && $this->count > 0 ){ 932 | return $this->count; 933 | } 934 | 935 | $this->preCheck(); 936 | list($_ret) = $this->cmd('STAT',null,true); 937 | $ret = explode(' ',$_ret); 938 | $this->count = $ret[1]; 939 | return $this->count; 940 | } 941 | 942 | function getMailAll(){ 943 | $this->preCheck(); 944 | ( 0 !== $this->count ) or $this->count(); 945 | $mail =array(); 946 | for($i = 1 ; $i <= $this->count ; $i ++){ 947 | $this->getMail(); 948 | } 949 | return $mail; 950 | } 951 | // OverRide Parent by POP 952 | function getMail(){ 953 | 954 | $this->_before_getMail(); 955 | 956 | if( -1 !== $this->pop_high_speed ){ 957 | $all = $this->getTop( null , $this->pop_high_speed ); 958 | }else{ 959 | $this->preCheck(); 960 | ( 0 !== $this->count ) or $this->count(); 961 | if( ( 0 >= $this->pointer) || ( $this->pointer > $this->count ) ){ 962 | return false; 963 | } 964 | $mail = $this->cmd('RETR', $this->pointer , false , false ); 965 | !$this->delete or $this->delete($i); 966 | $all = $mail[1]; 967 | } 968 | 969 | $this->set($all); 970 | $this->_after_getMail(); 971 | return $all; 972 | } 973 | 974 | function getTop( $msg_num = null , $line_num = null ){ 975 | $this->preCheck(); 976 | ( 0 !== $this->count ) or $this->count(); 977 | if( ( 0 >= $this->pointer) || ( $this->pointer > $this->count ) ){ 978 | return false; 979 | } 980 | $msg_num = is_null( $msg_num ) ? $this->pointer : $msg_num; 981 | $line_num = is_null( $line_num ) ? 0 : $line_num; 982 | $mail = $this->cmd( 'TOP' , $msg_num . ' ' . $line_num , false , false ); 983 | return $mail[1]; 984 | } 985 | 986 | function getUid( $msg_num = null ){ 987 | 988 | $this->preCheck(); 989 | ( 0 !== $this->count ) or $this->count(); 990 | if( ( 0 >= $this->pointer) || ( $this->pointer > $this->count ) ){ 991 | return false; 992 | } 993 | $msg_num = is_null( $msg_num ) ? $this->pointer : $msg_num; 994 | $mail = $this->cmd( 'UIDL' , $msg_num , true , false ); 995 | $_id = explode(' ',$mail[0]); 996 | if( '+OK' === strtoupper( trim( $_id[0] ) ) ){ 997 | $num = $_id[1]; 998 | $id = $_id[2]; 999 | }else{ 1000 | $num = $msg_num; 1001 | $id = null; 1002 | } 1003 | if( ( (int) $num != (int) $msg_num ) || empty($id) ){ 1004 | return false; 1005 | } 1006 | return $id; 1007 | } 1008 | 1009 | function getUidAll(){ 1010 | $this->preCheck(); 1011 | $mail = $this->cmd( 'UIDL' , null , false , true ); 1012 | $_id = explode(' ',array_shift($mail)); 1013 | if( '+OK' === strtoupper( trim( $_id[0] ) ) && 0 < count($mail) ){ 1014 | foreach($mail as $ma){ 1015 | if( '.' === $ma ){ // fool proof 1016 | break; 1017 | } 1018 | $temp = explode(' ',$ma); 1019 | $this->uid_list[$temp[0]] = trim( $temp[1] ); 1020 | } 1021 | return true; 1022 | }else{ 1023 | return false; 1024 | } 1025 | } 1026 | 1027 | function uidToNum( $uid ){ 1028 | if( 0 === count( $this->uid_list ) ){ 1029 | $this->getUidAll(); 1030 | } 1031 | return array_search( $uid , $this->uid_list) ; 1032 | } 1033 | 1034 | 1035 | function reset( $msg_num = null , $line_num = null ){ 1036 | $this->preCheck(); 1037 | $mail = $this->cmd( 'RSET' , true , false ); 1038 | return $mail[0]; 1039 | } 1040 | function next(){ 1041 | $this->alreadyReset(); 1042 | return $this->pointer('++'); 1043 | } 1044 | function prev(){ 1045 | $this->alreadyReset(); 1046 | return $this->pointer('--'); 1047 | } 1048 | function pointer( $param = null ){ 1049 | $this->alreadyReset(); 1050 | if(is_null($param)){ 1051 | return $this->pointer; 1052 | } 1053 | if(is_numeric($param)){ 1054 | $this->pointer = $param ; 1055 | }elseif('++'===$param){ 1056 | $this->pointer++; 1057 | }elseif('--'===$param){ 1058 | $this->pointer--; 1059 | }else{ 1060 | return false; 1061 | } 1062 | if( ( 0 >= $this->pointer) || ( $this->pointer > $this->count ) ){ 1063 | return false; 1064 | } 1065 | return true; 1066 | } 1067 | 1068 | function delete( $num = null , $done = false ){ 1069 | $num = is_null($num) ? $this->pointer : $num ; 1070 | if(!is_numeric($num)){ 1071 | $this->error( 'Specifed Error , delete command needs numeric' , __LINE__ ); 1072 | } 1073 | 1074 | $ret1 = !( false === $this->cmd('DELE', $num , true ) ); 1075 | $ret2 = true; 1076 | if( $done ){ 1077 | $ret2 = $this->done(); 1078 | } 1079 | 1080 | return $ret1 && $ret2; 1081 | } 1082 | 1083 | function deleteUid( $uid ){ 1084 | if( !is_array($uid) ){ 1085 | $uid = array( $uid ); 1086 | } 1087 | $fg = true; 1088 | $this->pointer(1); 1089 | $uid = array_flip( $uid ); 1090 | $max = $this->count(); 1091 | for($i = 1 ; $i <= $max ; $i++ ){ 1092 | $key=$this->getUid($i); 1093 | if( isset($uid[trim($key)]) ){ 1094 | $fg = $this->delete( $i ) && $fg; 1095 | } 1096 | } 1097 | return ; 1098 | } 1099 | 1100 | function listHeader(){ 1101 | 1102 | $parameter = func_get_args(); 1103 | 1104 | foreach($parameter as $key => $param){ 1105 | if(is_string($param)){ 1106 | $parameter[$key] = array( $param ); 1107 | } 1108 | } 1109 | 1110 | $ret = array(); 1111 | $max = $this->count(); 1112 | $stack = $this->popHigh(); 1113 | $this->popHigh(0); 1114 | for( $i = 1 ; $i <= $max ; $i ++ ){ 1115 | $this->pointer($i); 1116 | foreach($parameter as $key => $param){ 1117 | $ret[$i][$param[0]] = $this->header( $param , null ); 1118 | } 1119 | } 1120 | $this->popHigh($stack); 1121 | return $ret; 1122 | 1123 | } 1124 | 1125 | function eof(){ 1126 | $ct = $this->count(); 1127 | $pt = $this->pointer(); 1128 | return empty( $ct ) || empty( $pt ) || ( $pt > $ct ) ; 1129 | } 1130 | 1131 | //-------------------------------------------- 1132 | // for Communication to POP Server 1133 | //-------------------------------------------- 1134 | function cmd( $cmd , $param=null , $line_1=false , $array = true ){ 1135 | $cmd .= isset($param) ? ' '.$param:null; 1136 | list( $fg , $ret ) = $this->communicate( $cmd , array('Error Command '.$cmd,__LINE__,true) , $line_1,$array); 1137 | 1138 | if(false===$fg){ 1139 | $ret = array( false , false ); 1140 | } 1141 | 1142 | return $ret; 1143 | } 1144 | 1145 | function communicate( $put_message , $err = null , $line_1 = false , $array=true){ 1146 | if(!$this->preCheck()){ 1147 | return false; 1148 | } 1149 | 1150 | $this->debugEchoLine('Client: ',$put_message); 1151 | fputs( $this->fp , $put_message."\r\n"); 1152 | $ret = $this->getMessage($line_1 , $array ); 1153 | if( '+OK' === strtoupper( substr( $ret[0] , 0 , 3 ) ) ){ 1154 | $fg = true; 1155 | }else{ 1156 | $er = print_r(stream_get_meta_data ( $this->fp ) , true ); 1157 | $this->error( 'Comunicate Error, stream_get_meta_data=>'.$er ,__LINE__); 1158 | if(isset($err)){ 1159 | $this->error( $err[0] , isset($err[1]) ? $err[1]:null , isset($err[2]) ? $err[2]:null ); 1160 | } 1161 | $fg = false; 1162 | } 1163 | return array( $fg , $ret ); 1164 | } 1165 | 1166 | function getMessage( $line_1 = false , $array = true){ 1167 | 1168 | if(!$this->preCheck()){ 1169 | return false; 1170 | } 1171 | $r = fgets( $this->fp , 512 ); 1172 | $this->debugEchoLine('Server: ',$r); 1173 | 1174 | 1175 | if( ('.' === substr( $r , -1 )) || $line_1 ) { 1176 | return array($r); 1177 | } 1178 | 1179 | $ret = array( 0 => $r ); 1180 | if(!$array){ 1181 | $ret[1] = null; 1182 | } 1183 | $r = fgets( $this->fp , 512 ); 1184 | $this->debugEchoLine('Server: ',$r); 1185 | while(( '.' !=trim($r) ) && ( false !== $r ) && ( !feof($this->fp)) ){ 1186 | if( '.'===substr($r,0,1) && '.' !==$r){ 1187 | $r = substr($r,1); 1188 | } 1189 | if($array){ 1190 | $ret[] = $r ; 1191 | }else{ 1192 | $ret[1] .= $r ; 1193 | } 1194 | $r = fgets($this->fp , 512 ); 1195 | $this->debugEchoLine('Server: ',$r); 1196 | } 1197 | if(isset($ret[1])){ 1198 | $ret[1] = $array ? $ret[1] : trim( $ret[1]."\r\n\r\n" ); 1199 | } 1200 | return $ret; 1201 | } 1202 | } 1203 | //--------------------------------- 1204 | // Normal QdPop 1205 | //--------------------------------- 1206 | class QdPop extends QdPopBase{ 1207 | 1208 | var $name = 'QdPop'; 1209 | 1210 | function QdPop( $param = null ){ 1211 | parent::__construct( $param ); 1212 | } 1213 | 1214 | } 1215 | 1216 | class QdDecodeStdin extends QdDecodeBase{ 1217 | 1218 | var $name = 'QdDecodeStdin'; 1219 | 1220 | function __construct( $param = null ){ 1221 | $this->QdDecodeStdin($param); 1222 | } 1223 | function QdDecodeStdin( $param = null ){ 1224 | parent::__construct( $param ); 1225 | } 1226 | 1227 | function getMail(){ 1228 | 1229 | $this->_before_getMail(); 1230 | 1231 | $content = null; 1232 | $fp=fopen("php://stdin",'r'); 1233 | 1234 | if(!is_resource($fp)){ 1235 | return $this->error("no resouce",__LINE__,false); 1236 | } 1237 | while( !feof($fp) ){ 1238 | $content .= fgets( $fp ,1024); 1239 | } 1240 | $this->set($content); 1241 | 1242 | $this->_after_getMail(); 1243 | return $content; 1244 | } 1245 | } 1246 | class QdDecodeDirect extends QdDecodeBase{ 1247 | 1248 | var $name = 'QdDecodeDirect'; 1249 | 1250 | function __construct( $param = null ){ 1251 | $this->QdDecodeDirect($param); 1252 | } 1253 | function QdDecodeDirect( $param = null ){ 1254 | if( !empty( $param[0] ) ){ 1255 | $this->set($param[0]); 1256 | } 1257 | parent::__construct( $param ); 1258 | } 1259 | 1260 | function getMail(){ 1261 | 1262 | $this->_before_getMail(); 1263 | 1264 | $content = $this->all(); 1265 | 1266 | $this->_after_getMail(); 1267 | return $content; 1268 | } 1269 | } 1270 | //********************************************************** 1271 | // 1272 | // 1273 | // Main Class (Controll Class) 1274 | // 1275 | // 1276 | //********************************************************** 1277 | class QdmailReceiver extends QdmailReceiverError{ 1278 | 1279 | var $name = 'QdmailReceiver'; 1280 | 1281 | static function start(){ 1282 | $type_link = array( 1283 | 'stdin' => 'QdDecodeStdin', 1284 | 'pop' => 'QdPop', 1285 | 'direct'=> 'QdDecodeDirect', 1286 | ); 1287 | 1288 | $param = func_get_args(); 1289 | $type = $param[0]; 1290 | 1291 | if( is_array( $param[0] ) ){ 1292 | $param[0] = array_change_key_case( $param[0] , CASE_LOWER ); 1293 | if( isset( $param[0]['type'] ) ){ 1294 | $type = $param[0]['type']; 1295 | }else{ 1296 | $type = key($type_link); 1297 | } 1298 | }else{ 1299 | array_shift( $param ); 1300 | } 1301 | $type = strtolower($type); 1302 | if(!isset($type_link[$type])){ 1303 | return false; 1304 | } 1305 | $param['name'] = 'QdmailReceiver'; 1306 | return QdmailReceiver::getInstance( $type_link[$type] , $param ); 1307 | } 1308 | 1309 | static function & getInstance( $class_name , $param = null){ 1310 | $version = (float) PHP_VERSION ; 1311 | if( 5 > $version ){ 1312 | $obj = & new $class_name($param); 1313 | }else{ 1314 | $obj = new $class_name($param); 1315 | } 1316 | return $obj; 1317 | } 1318 | 1319 | } 1320 | 1321 | 1322 | //********************************************************** 1323 | // 1324 | // 1325 | // Function Lists 1326 | // 1327 | // 1328 | //********************************************************** 1329 | 1330 | if(!function_exists('qd_decode')){ 1331 | function qd_decode( $item , $charset = null, $option = null ){ 1332 | return qd_receive_mail( 'set' , 'direct' , $charset , $option ); 1333 | } 1334 | } 1335 | 1336 | if(!function_exists('qdrm')){ 1337 | function Qdrm( $cmd , $param = null , $charset = null){ 1338 | return qd_receive_mail( $cmd , $param , $charset ); 1339 | } 1340 | } 1341 | if(!function_exists('qd_receive_mail')){ 1342 | function qd_receive_mail( $cmd , $param = null , $charset = null){ 1343 | 1344 | static $receive = array(); 1345 | static $error = array(); 1346 | static $method = array( 1347 | 'all' => 'all', 1348 | 'next' => 'next', 1349 | 'prev' => 'prev', 1350 | 'count' => 'count', 1351 | 'pointer' => 'pointer', 1352 | 'done' => 'done', 1353 | 'timeout' => 'timeout', 1354 | 'debug' => 'debug', 1355 | 'attach' => 'attach', 1356 | 'eof' => 'eof', 1357 | 'bodyall' => 'body', 1358 | 'body' => 'bodyAutoSelect', 1359 | 'text' => 'text', 1360 | 'html' => 'html', 1361 | 'ishtml' => 'isHtml', 1362 | 'header' => 'header', 1363 | 'head' => 'header', 1364 | 'deleteuid' => 'deleteUid', 1365 | 'deluid' => 'deleteUid', 1366 | 'pophigh' => 'popHigh', 1367 | 'delete' => 'delete', 1368 | 'del' => 'delete', 1369 | 'listheader'=> 'listHeader', 1370 | ); 1371 | 1372 | 1373 | $cmd = strtolower( $cmd ); 1374 | if( 'pop' == $cmd ){ 1375 | $receive[0] = QdmailReceiver::start( 'pop' , $param , $charset); 1376 | } 1377 | if( 'stdin' == $cmd ){ 1378 | $receive[0] = QdmailReceiver::start( 'stdin' , $param , $param ); 1379 | } 1380 | if( 'direct' == $cmd ){ 1381 | $receive[0] = QdmailReceiver::start( 'direct' , $param , $charset); 1382 | } 1383 | if( !is_object( $receive[0] ) ){ 1384 | return false; 1385 | } 1386 | if( isset( $charset ) ){ 1387 | $receive[0] -> charser( $charset ); 1388 | } 1389 | if( 'error' == $cmd ){ 1390 | return $error; 1391 | } 1392 | if( 'start' == $cmd ){ 1393 | return true; 1394 | } 1395 | 1396 | if( !isset($method[$cmd]) || !method_exists ( $receive[0] , $method[$cmd] )){ 1397 | $error[] = 'Call no decleared Method \''.$cmd.'\''; 1398 | return false; 1399 | } 1400 | return $receive[0]->{$method[$cmd]}($param); 1401 | } 1402 | } 1403 | 1404 | -------------------------------------------------------------------------------- /qdsmtp.php: -------------------------------------------------------------------------------- 1 | error; 47 | } 48 | if( is_null( $lf ) ){ 49 | $lf = $this->log_LFC ; 50 | } 51 | if( !is_array( $error ) ){ 52 | $error = array( $error ); 53 | } 54 | $out = null ; 55 | foreach($error as $mes){ 56 | $out .= $this->name . ' error: ' . trim( $mes ) . $lf ; 57 | } 58 | if( $this->error_display && $display ){ 59 | $_out = str_replace( $lf ,'
' . $lf , $out ); 60 | echo $_out ; 61 | } 62 | return $out ; 63 | } 64 | 65 | function errorGather( $message = null , $line = null){ 66 | 67 | if( !is_null( $message ) && !$this->error_ignore){ 68 | if( !is_null( $line ) ){ 69 | $message .= ' line -> '.$line; 70 | } 71 | $this->error[] = $message ; 72 | }elseif( 0 === count( $this->error ) ){ 73 | return true; 74 | }elseif( $this->error_ignore ){ 75 | return false; 76 | } 77 | 78 | $er = $this->errorRender(); 79 | $this->error_stack = array_merge( $this->error_stack , $this->error ); 80 | $this->error = array(); 81 | if( !$this->logWrite( 'error' , $er )){ 82 | $this->error_stack = array_merge( $this->error_stack , $this->error ); 83 | } 84 | return false; 85 | } 86 | 87 | function logWrite( $type , $message ){ 88 | $tp = ('error' == $type) ? false:true; 89 | $level = $tp ? $this->log_level:$this->errorlog_level; 90 | if( 0 == $level ){ 91 | return true; 92 | } 93 | $filename = $tp ? $this->log_filename:$this->errorlog_filename; 94 | $ap = $tp ? $this->log_append:$this->errorlog_append; 95 | $fp = fopen( $filename , $ap ); 96 | if( !is_resource( $fp ) ){ 97 | $this->error[]='file open error at logWrite() line->'.__LINE__; 98 | return false; 99 | } 100 | $spacer = $tp ? $this->log_LFC : $this->log_LFC ; 101 | fwrite( $fp , 102 | date( $this->log_dateformat ) 103 | . $spacer 104 | . trim( $message ) 105 | . $this->log_LFC 106 | ); 107 | fclose( $fp ) ; 108 | return true ; 109 | } 110 | function log(){ 111 | $mes = null; 112 | foreach($this->smtp_log as $line){ 113 | $mes .= trim( $line ) . $this->log_LFC; 114 | } 115 | $this->logWrite( null ,$mes ); 116 | $this->smtp_log = array(); 117 | } 118 | function logFilename( $data = null ){ 119 | if( is_null( $data ) ){ 120 | return $this->log_filename; 121 | } 122 | if( is_string( $data ) ){ 123 | $this->log_filename = $data; 124 | return $this->errorGather(); 125 | }else{ 126 | return $this->errorGather('Data specified error',__LINE__); 127 | } 128 | } 129 | function errorlogFilename( $data = null ){ 130 | if( is_null( $data ) ){ 131 | return $this->errorlog_filename; 132 | } 133 | if( is_string( $data ) ){ 134 | $this->errorlog_filename = $data; 135 | return $this->errorGather(); 136 | }else{ 137 | return $this->errorGather('Data specified error',__LINE__); 138 | } 139 | } 140 | } 141 | 142 | class QdsmtpBase extends QdsmtpError{ 143 | 144 | var $name = 'QdSmtp'; 145 | var $smtpLFC ="\r\n"; 146 | var $smtp_param = array( 147 | 'HOST' => null, 148 | 'FROM' => null, 149 | 'USER' => null, 150 | 'PASS' => null, 151 | 'PORT' => 25, 152 | 'PROTOCOL' => array( 153 | 'SMTP_AUTH','POP_BEFORE','SMTP' 154 | ), 155 | 'POP_SERVER'=> null, 156 | 'POP_USER' => null, 157 | 'POP_PASS' => null, 158 | 'CONTINUE' => null, 159 | ); 160 | var $protocol_def ='SMTP'; 161 | var $smtp_log = array(); 162 | var $smtp_auth_kind = array('PLAIN'); 163 | // var $smtp_auth_kind = array('CRAM-MD5','DIGEST-MD5','LOGIN','PLAIN'); 164 | var $data = null; 165 | var $continue = false; 166 | var $auto_kind = true; 167 | var $rcpt = array(); 168 | var $rcpt_stack = array(); 169 | var $rcpt_undone = array(); 170 | var $rcpt_undone_stack = array(); 171 | var $smtp_limit = 1000; 172 | var $sock = null; 173 | var $already_auth = false; 174 | // POP3 175 | var $pop3_time_file = 'qdsmtp.time'; 176 | var $pop3_use_file = true; 177 | var $pop3_connect_start = null; 178 | var $pop3_valid_minute = 10; 179 | var $time_out = 3 ; 180 | var $always_notify_success = false; 181 | 182 | function QdsmtpBase( $param = null ){ 183 | if( !is_null( $param[0] ) && is_bool( $param[0] ) ){ 184 | $this->continue = $continue; 185 | } 186 | if( is_array( $param[0] ) ){ 187 | $this->server( $param[0] ); 188 | } 189 | } 190 | 191 | //----------------------------------------- 192 | // User Interface except POP3 193 | //----------------------------------------- 194 | function server( $param = null ){ 195 | if( is_null( $param ) ){ 196 | return $this->smtp_param; 197 | }elseif( !is_array( $param ) ){ 198 | return $this->errorGather('Error Parameter type',__LINE__); 199 | } 200 | $param = array_change_key_case( $param , CASE_UPPER ); 201 | if( isset( $param['PROTOCOL'] ) && is_array($param['PROTOCOL'] ) ){ 202 | $param['PROTOCOL'] = array_change_key_case( $param['PROTOCOL'] , CASE_UPPER ); 203 | } 204 | if( isset( $param['ALWAYS_NOTIFY'] ) && is_bool($param['ALWAYS_NOTIFY']) ){ 205 | $this->always_notify_success = $param['ALWAYS_NOTIFY']; 206 | } 207 | $this->smtp_param = array_merge( $this->smtp_param , $param ); 208 | if( isset( $this->smtp_param['CONTINUE'] ) ){ 209 | $this->continue = $this->smtp_param['CONTINUE']; 210 | } 211 | return true; 212 | } 213 | function data( $data = null ){ 214 | if( is_null( $data ) ){ 215 | return $this->data; 216 | } 217 | if( is_string( $data ) ){ 218 | $this->data = $data; 219 | return $this->errorGather(); 220 | }else{ 221 | return $this->errorGather('Data specified error',__LINE__); 222 | } 223 | } 224 | function to( $recip , $add = false ){ 225 | return $this->recipient( $recip ); 226 | } 227 | function recipient( $recip , $add = false ){ 228 | if( !is_array( $recip ) ){ 229 | $recip = array( $recip ); 230 | } 231 | if( $add ){ 232 | $this->recipient = array_merge( $this->recipient , $recip ); 233 | $this->recipient = array_unique( $this->recipient ); 234 | }else{ 235 | $this->recipient = $recip; 236 | } 237 | return $this->errorGather(); 238 | } 239 | function addRecipient( $recip ){ 240 | return $this->recipient( $recip , true ); 241 | } 242 | function addto( $recip ){ 243 | return $this->addRecipient( $recip ); 244 | } 245 | function done( $stack = false ){ 246 | if( $stack ){ 247 | return $this->rcpt_stack; 248 | }else{ 249 | return $this->rcpt; 250 | } 251 | } 252 | function undone( $stack = false ){ 253 | if( $stack ){ 254 | return $this->rcpt_undone_stack; 255 | }else{ 256 | return $this->rcpt_undone; 257 | } 258 | } 259 | function continueConnect( $bool = null ){ 260 | if( is_null( $bool ) ){ 261 | return $this->continue; 262 | } 263 | if( is_bool( $bool ) ){ 264 | $this->continue = $bool; 265 | return $this->errorGather(); 266 | }else{ 267 | return $this->errorGather('Connection Continue Mode specifed error',__LINE__); 268 | } 269 | } 270 | function timeOut( $sec = null ){ 271 | if(is_null($sec)){ 272 | return $this->time_out; 273 | } 274 | if( is_numeric( $sec ) ){ 275 | $this->time_out = $sec; 276 | return $this->errorGather(); 277 | }else{ 278 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 279 | } 280 | } 281 | function errorlogLevel( $num = null ){ 282 | if( is_null( $num ) ){ 283 | return $this->errorlog_level; 284 | } 285 | if( is_numeric( $num ) ){ 286 | $this->errorlog_level = $num; 287 | return $this->errorGather(); 288 | }else{ 289 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 290 | } 291 | } 292 | function logLevel( $num = null ){ 293 | if( is_null( $num ) ){ 294 | return $this->log_level; 295 | } 296 | if( is_numeric( $num ) ){ 297 | $this->log_level = $num; 298 | return $this->errorGather(); 299 | }else{ 300 | return $this->errorGather(__FUNCTION__.' specifed error',__LINE__); 301 | } 302 | } 303 | function alwaysNotifySuccess( $bool = null ){ 304 | if(is_null($bool)){ 305 | return $this->always_notify_success; 306 | } 307 | if(is_bool($bool)){ 308 | $this->always_notify_success = $bool; 309 | return true; 310 | }else{ 311 | return false; 312 | } 313 | } 314 | //------------------------------------------ 315 | // Sending Method 316 | //------------------------------------------ 317 | // Qdsmtp ignore $option parameter 318 | function mail( $to , $subject , $message , $header= null , $option = null ){ 319 | $this->makeData( $to , $subject , $message , $header , $option ); 320 | return $this->send(); 321 | } 322 | 323 | function send( $data = null ){ 324 | 325 | if( !is_array($this->smtp_param['PROTOCOL'])){ 326 | $fg = $this->sendBase( $data , $this->smtp_param['PROTOCOL'] ); 327 | $this->log(); 328 | return $fg; 329 | } 330 | $stack = array( $this->error_display , $this->errorlog_level ); 331 | $this->error_display = false; 332 | $this->errorlog_level= 0; 333 | $ret = false; 334 | foreach($this->smtp_param['PROTOCOL'] as $protocol ){ 335 | if( $this->sendBase( $data , $protocol ) ){ 336 | $ret = true; 337 | $this->error_stack = array(); 338 | break; 339 | } 340 | } 341 | list( $this->error_display , $this->errorlog_level ) = $stack; 342 | if( !$ret ){ 343 | $fg = $this->errorGather( implode($this->smtpLFC , $this->error_stack) , __LINE__); 344 | $this->log(); 345 | return $fg; 346 | } 347 | if( !$this->continue ){ 348 | $this->close(); 349 | } 350 | $this->log(); 351 | return ( 0 === count( $this->error_stack ) ) && $this->errorGather(); 352 | } 353 | 354 | function close(){ 355 | $items = array( 356 | array( 'QUIT' , null ), 357 | ); 358 | list( $st , $mes , $com ) = $this->communicate( $items ); 359 | if( !$st ){ 360 | return $this->errorGather('Error at QUIT',__LINE__); 361 | } 362 | fclose( $this->sock ); 363 | } 364 | 365 | function sendBase( $data = null ,$protocol = null ){ 366 | if( !is_null( $data ) ){ 367 | $this->data = $data; 368 | } 369 | if( is_null( $protocol ) ){ 370 | $protocol = $this->protocol_def; 371 | } 372 | switch($protocol){ 373 | case 'POP_BEFORE'://POP3 374 | if( !$this->pop3() ){ 375 | return $this->errorGather('POP failure',__LINE); 376 | } 377 | case 'SMTP': 378 | if( !is_resource( $this->sock ) ){ 379 | $this->sock = $this->connect(); 380 | } 381 | $this->sayHello(); 382 | if( !$this->sendData() ){ 383 | return false; 384 | } 385 | break; 386 | case 'SMTP_AUTH': 387 | if(!is_resource($this->sock)){ 388 | $this->sock = $this->connect(); 389 | } 390 | 391 | if( !$this->already_auth ){ 392 | $this->sayHello(); 393 | if( 0 === preg_match('/[\s-]AUTH\s+([^\r\n]*)\r?\n/is', implode($this->smtpLFC,$this->smtp_log) , $matches)){ 394 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' doesnot suppoted SMTP AUTH Protocol',__LINE__); 395 | } 396 | $mes = strtoupper( $matches[1] ); 397 | $decide = null; 398 | foreach($this->smtp_auth_kind as $auth){ 399 | if( false !== strpos( $mes , $auth ) ){ 400 | $decide = $auth; 401 | break; 402 | } 403 | } 404 | if( is_null( $decide ) ){ 405 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' doesnot suppoted MY Abalable SMTP AUTH Protocol '.implode(' or ',$this->smtp_auth_kind),__LINE__); 406 | } 407 | $decide = strtolower( str_replace( '-' , '_' ,$decide ) ); 408 | if( !$this->{$decide}() ){ 409 | return $this->errorGather('Auth Error',__LINE__);; 410 | } 411 | $this->already_auth = true; 412 | } 413 | if( !$this->sendData() ){ 414 | $this->already_auth = false; 415 | return $this->errorGather('Send Data Error or Auth Error',__LINE__); 416 | } 417 | break; 418 | case 'OVER_SSL': 419 | break; 420 | default: 421 | break; 422 | } 423 | //debug 424 | /* 425 | if( 0==count($this->error_stack)){ 426 | $this->error_stack = 'no error'; 427 | } 428 | echo "
";
429 | 		echo htmlspecialchars(print_r($this->smtp_log , true));
430 | 		echo htmlspecialchars(print_r($this->error_stack , true));
431 | 		echo "
"; 432 | */ 433 | return $this->errorGather(); 434 | 435 | } 436 | 437 | 438 | //---------------------------------------------- 439 | // Comunication 440 | //---------------------------------------------- 441 | //------------------------------------------------------------------------- 442 | // see RFC821 443 | // in japanese ->http://www.sea-bird.org/doc/rfc_doc/rfc821-jp.txt(not me) 444 | //------------------------------------------------------------------------- 445 | var $smtp_status100 = array( 446 | 1 => 4, 447 | 2 => 3, 448 | 3 => 2, 449 | 4 => 1, 450 | 5 => 0, 451 | ); 452 | // 0 error 453 | // 1 continue 454 | // 2 final 455 | var $smtp_status10 = array( 456 | 0 => 0, 457 | 1 => 5, 458 | 2 => 1, 459 | 3 => 0, 460 | 4 => 0, 461 | 5 => 5, 462 | ); 463 | 464 | function sayHello(){ 465 | $items = array( 466 | array( 'EHLO' , $this->smtp_param['HOST'] ), 467 | array( 'HELO' , $this->smtp_param['HOST'] ), 468 | ); 469 | if( !$this->tryUntilSuccess( $items ) ){ 470 | return $this->errorGather('HOST:'.$this->smtp_param['HOST'].' say no HELLO',__LINE__); 471 | } 472 | return $this->errorGather(); 473 | } 474 | function sendData(){ 475 | $reci = array(); 476 | 477 | if( '<' == substr( $this->smtp_param['FROM'],0,1) ){ 478 | $from = 'FROM:' . $this->smtp_param['FROM']; 479 | }else{ 480 | $from = 'FROM:<' . $this->smtp_param['FROM'] . '>'; 481 | } 482 | 483 | $items = array( 484 | array( 'MAIL' , $from ), 485 | ); 486 | list( $st , $mes , $com ) = $this->communicate($items); 487 | if( !$st ){ 488 | return $this->errorGather('Error From setting',__LINE__); 489 | } 490 | $this->rcpt = array(); 491 | $notify = $this->always_notify_success ? ' NOTIFY=SUCCESS,FAILURE':''; 492 | foreach( $this->recipient as $recipi ){ 493 | $items = array(array( 'RCPT' , 'TO:<' . $recipi . '>' . $notify )); 494 | list( $st , $mes , $com ) = $this->communicate($items); 495 | if( !$st ){ 496 | $this->rcpt_undone[] = $recipi ; 497 | $this->errorGather('Error RCPT setting',__LINE__); 498 | }else{ 499 | $this->rcpt[] = $recipi ; 500 | } 501 | } 502 | $this->rcpt_stack = array_merge( $this->rcpt_stack , $this->rcpt ); 503 | $this->rcpt_undone_stack = array_merge( $this->rcpt_undone_stack , $this->rcpt_undone ); 504 | $items = array(); 505 | $items[] = array( 'DATA' , null ); 506 | $items[] = array( 'DATA_CONTENT' , $this->smtpEscape($this->data).$this->smtpLFC . '.' ); 507 | $items[] = array( 'RSET' , null ); 508 | list( $st , $mes , $com ) = $this->communicate($items); 509 | if( !$st ){ 510 | return $this->errorGather('Error Data sending',__LINE__); 511 | } 512 | return $this->errorGather(); 513 | } 514 | 515 | 516 | function communicate( $items , $fp = null ){ 517 | if( is_null( $fp ) ){ 518 | $fp = $this->sock ; 519 | } 520 | $message = null ; 521 | if( !is_resource( $fp ) ){ 522 | return array( $this->errorGather( 'Error Resouce or stop connect' ,__LINE__) , $message , false ); 523 | } 524 | foreach( $items as $item ){ 525 | if( 'DATA_CONTENT' == $item[0] ){ 526 | $spacer = null ; 527 | $item[0] = null ; 528 | }elseif( 'DATA' == $item[0] || is_null($item[1]) ){ 529 | $spacer = null; 530 | }else{ 531 | $spacer = ' '; 532 | } 533 | $put_message = rtrim( $item[0] . $spacer . $item[1] ) . $this->smtpLFC; 534 | if( !fputs( $fp , $put_message ) ){ 535 | return array( $this->errorGather('SMTP can not fputs',__LINE__), $message , false ); 536 | } 537 | $this->smtp_log[] = $this->name . ' ' . $put_message ; 538 | 539 | do{ 540 | list( $st , $_message ) = $this->getMessage( $fp ); 541 | $message .= trim( $_message ) . $this->smtpLFC; 542 | if( !$st ){ 543 | return array( $this->errorGather('getMessage error',__LINE__), $message , $st ); 544 | } 545 | $branch = isset($this->smtp_status[$item[0]][$st]) ? $this->smtp_status[$item[0]][$st] : null; 546 | switch( $branch ){ 547 | case 'S': // Success 548 | $contine = false ; 549 | break; 550 | case 'F': // Failure 551 | return array( $this->errorGather('Failure :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 552 | break; 553 | case 'E': // Error 554 | return array( $this->errorGather('Error :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 555 | break; 556 | default: 557 | $s100 = (int) substr( $st , 0 , 1 ); 558 | $s10 = (int) substr( $st , 1 , 1 ); 559 | $s = $this->smtp_status100[$s100] * $this->smtp_status10[$s10]; 560 | switch($s){ 561 | case 0: // Error 562 | return array( $this->errorGather('Unkown Error :status'.$st.' message:'.htmlspecialchars($_message).' on '.htmlspecialchars($put_message),__LINE__) , $message , $st ); 563 | break; 564 | case 3: //22X,220 565 | $contine = true ; 566 | break; 567 | case 10: //35X,354 568 | $contine = false ; 569 | break; 570 | case 15: //25X,250 Sucsess 571 | $contine = false ; 572 | break; 573 | default: 574 | $contine = false; 575 | break; 576 | } 577 | break; 578 | } 579 | }while($contine); 580 | } 581 | return array($this->errorGather() , $message , $st ); 582 | } 583 | 584 | function getMessage( $fp = null ){ 585 | if( is_null( $fp ) ){ 586 | $fp = $this->sock ; 587 | } 588 | if( !is_resource( $fp ) ){ 589 | return array( $this->errorGather( 'Error Resouce or stop connect' ,__LINE__) , null ); 590 | } 591 | $status = array(); 592 | $status[-1] = null; 593 | $message = array(); 594 | $count = 0; 595 | do{ 596 | $mes = fgets( $fp , 512 ); 597 | if( false === $mes ){ 598 | $er = stream_get_meta_data ( $fp ); 599 | $er_mes = null; 600 | if( true === $er ['timed_out'] ){ 601 | $er_mes = ' SYSTEM TIME OUT '; 602 | } 603 | return array( $this->errorGather('No Responce' . $er_mes,__LINE__) , null ); 604 | } 605 | $status[$count] = substr( $mes , 0 , 3 ); 606 | $_continue = substr( $mes , 3 , 1 ); 607 | $message[] = trim( $mes ); 608 | $this->smtp_log[] = 'Server '.$mes ; 609 | if( '-' == $_continue && ( ($status[$count] == $status[$count-1]) || (0 == $count)) ){ 610 | $continue = true ; 611 | }else{ 612 | $continue = false ; 613 | } 614 | $count ++; 615 | }while($continue); 616 | return array( $status[0] , implode( $this->smtpLFC , $message ) ); 617 | } 618 | 619 | function connect( $host = null , $port = null , $status = 220 ){ 620 | if(is_null($host)){ 621 | $host = $this->smtp_param['HOST']; 622 | } 623 | if(is_null($port)){ 624 | $port = $this->smtp_param['PORT']; 625 | } 626 | $sock = fsockopen( $host , $port , $err , $errst , $this->time_out ); 627 | if( !is_resource( $sock ) ){ 628 | return $this->errorGather('Connection error HOST: '.$host.' PORT: ' . $port ,__LINE__); 629 | } 630 | stream_set_timeout ( $sock , $this->time_out ); 631 | return $sock; 632 | } 633 | 634 | function tryUntilSuccess( $items ){ 635 | $try = false; 636 | $err_mes = array(); 637 | foreach( $items as $item ){ 638 | $err_mes[] = $item[0]; 639 | $this->error_ignore = true; 640 | list( $st , $mes , $com ) = $this->communicate( array( $item ) ); 641 | $this->error_ignore = false; 642 | if( true === $st ){ 643 | $try = true; 644 | $this->error = array(); 645 | break; 646 | } 647 | } 648 | if( !$try ){ 649 | return $this->errorGather( 'Tyr Error ->' . implode( ' ' , $err_mes ) ,__LINE__); 650 | } 651 | return $this->errorGather(); 652 | } 653 | 654 | //-------------------------- 655 | // AUTH 656 | //-------------------------- 657 | function plain(){ 658 | $plain = $this->makePlain(); 659 | $items = array(); 660 | foreach($plain as $pn ){ 661 | $items[] = array( 'AUTH PLAIN' , $pn ); 662 | } 663 | return $this->tryUntilSuccess( $items ); 664 | } 665 | function makePlain(){ 666 | $plain[0] = base64_encode($this->smtp_param['USER']."\0".$this->smtp_param['USER']."\0".$this->smtp_param['PASS']); 667 | $plain[1] = base64_encode($this->smtp_param['USER']."\0".$this->smtp_param['PASS']); 668 | return $plain; 669 | } 670 | //----------------------------------- 671 | // Utility 672 | //----------------------------------- 673 | function makeData( $to , $subject , $message , $header=null , $option=null ){ 674 | $recip = array(); 675 | $recip = array_merge( $recip , $this->extractAddr( $to ) ); 676 | $recip = array_merge( $recip , $this->extractAddr( $this->extractHeader('CC' , $header) ) ); 677 | $recip = array_merge( $recip , $this->extractAddr( $this->extractHeader('BCC' , $header) ) ); 678 | $this->recipient( $recip ); 679 | $head = trim( 'To: ' . $to . $this->smtpLFC . 'Subject: ' . trim( $subject ) . $this->smtpLFC . trim($header) ); 680 | return $this->data = $head . $this->smtpLFC . $this->smtpLFC . $message ; 681 | } 682 | 683 | function extractAddr( $line ){ 684 | if(0===preg_match_all('/,]+)>?\s*,?\s*/',$line,$matches)){ 685 | return array(); 686 | }else{ 687 | return $matches[1]; 688 | } 689 | } 690 | 691 | function extractHeader( $section , $header){ 692 | if( 0===preg_match('/'.$section.': (.*)\r?\n[^\s]/is' , $header , $matches ) ){ 693 | return null; 694 | }else{ 695 | return str_replace( array( "\r" , "\n" , "\t" , ' ' ) , null , $matches[1] ); 696 | } 697 | } 698 | 699 | function smtpEscape( $mes ){ 700 | $mes = preg_replace( '/\r?\n\.\r?\n/is' , $this->smtpLFC . '..' . $this->smtpLFC , $mes ); 701 | if( 0 !== preg_match( '/\r?\n[^\r\n]{'.$this->smtp_limit.',}\r?\n/is' , $mes ) ){ 702 | return $this->errorGather('SMTP Overfllow '.$this->smtp_limit.' chars in one line.',__LINE__); 703 | } 704 | return $mes; 705 | } 706 | //---------------------------------------------------- 707 | // POP3 708 | //---------------------------------------------------- 709 | function pop3(){ 710 | 711 | if( $this->pop3_use_file ){ 712 | if( !file_exists( $this->pop3_time_file ) ){ 713 | $this->writePop3Time(); 714 | } 715 | $this->pop3_connect_start = file_get_contents( $this->pop3_time_file ); 716 | }elseif( is_null( $this->pop3_connect_start ) ){ 717 | $this->pop3_connect_start = time(); 718 | } 719 | if( ( $this->pop3_connect_start + $this->pop3_valid_minute * 60 ) > time() ){ 720 | return $this->errorGather(); 721 | } 722 | 723 | if( $this->pop3_use_file ){ 724 | $this->writePop3Time(); 725 | } 726 | $fp = $this->connect( $this->smtp_param['POP_HOST'] , 110 , '+OK' ); 727 | if( false === $fp ){ 728 | return $this->errorGather('Can not connect POP3 Sever' ,__LINE__); 729 | } 730 | $items = array( 731 | array( 'USER' , $this->smtp_param['POP_USER'] ), 732 | array( 'PASS' , $this->smtp_param['POP_PASS'] ), 733 | ); 734 | $this->communicate( $items , $fp ); 735 | fclose( $fp ); 736 | return $this->errorGather(); 737 | } 738 | function pop3UseFile( $bool = null ){ 739 | if( is_null($bool)){ 740 | return $this->pop3_use_file; 741 | } 742 | if( is_bool( $bool ) ){ 743 | $this->pop3_use_file = $bool; 744 | return $this->errorGather(); 745 | }else{ 746 | return $this->errorGather('POP3 UseFile specifed error',__LINE__); 747 | } 748 | } 749 | function pop3TimeFilename( $filename = null ){ 750 | if(is_null($filename)){ 751 | return $this->pop3_time_file; 752 | } 753 | if( is_string( $filename ) && !empty( $filename ) ){ 754 | $this->pop3_time_file = $filename; 755 | return $this->errorGather(); 756 | }else{ 757 | return $this->errorGather('POP3 Filename specifed error',__LINE__); 758 | } 759 | } 760 | function pop3ValidMinute( $min = null ){ 761 | if(is_null($min)){ 762 | return $this->pop3_valid_minute; 763 | } 764 | if( is_numeric( $min ) ){ 765 | $this->pop3_valid_minute = $min; 766 | return $this->errorGather(); 767 | }else{ 768 | return $this->errorGather('POP3 Valid Minute specifed error',__LINE__); 769 | } 770 | } 771 | function writePop3Time(){ 772 | $fp_time = fopen($this->pop3_time_file,'w'); 773 | fputs( $fp_time , time() ); 774 | fclose($fp_time); 775 | } 776 | //-------------------------------- 777 | // Result Colleciton 778 | //-------------------------------- 779 | var $smtp_status= array( 780 | 781 | 'USER' => array( 782 | '+OK' => 'S', // for pop3 783 | ), 784 | 'PASS' => array( 785 | '+OK' => 'S', // for pop3 786 | ), 787 | 788 | 'HELO' => array( 789 | '250' => 'S', 790 | '500' => 'E', 791 | '501' => 'E', 792 | '504' => 'E', 793 | '421' => 'E', 794 | ), 795 | 'EHLO' => array( 796 | '250' => 'S', 797 | '500' => 'E', 798 | '501' => 'E', 799 | '504' => 'E', 800 | '421' => 'E', 801 | ), 802 | 'MAIL' => array( 803 | '250' => 'S', 804 | '500' => 'E', 805 | '501' => 'E', 806 | '421' => 'E', 807 | '552' => 'E', 808 | '451' => 'E', 809 | '452' => 'E', 810 | ), 811 | 'RCPT' => array( 812 | '250' => 'S', 813 | '251' => 'S', 814 | '550' => 'F', 815 | '551' => 'F', 816 | '552' => 'F', 817 | '553' => 'F', 818 | '450' => 'F', 819 | '451' => 'F', 820 | '452' => 'F', 821 | '500' => 'E', 822 | '501' => 'E', 823 | '503' => 'E', 824 | '421' => 'E', 825 | ), 826 | 'DATA' => array( 827 | '354' => 'S', 828 | '250' => 'S', 829 | '451' => 'F', 830 | '554' => 'F', 831 | '500' => 'E', 832 | '501' => 'E', 833 | '503' => 'E', 834 | '421' => 'E', 835 | ), 836 | 'AUTH PLAIN' => array( 837 | '235' => 'S', 838 | '503' => 'S', //503 5.5.0 Already Authenticated 839 | '501' => 'E', 840 | '535' => 'E', 841 | ), 842 | 'STARTTLS' => array( 843 | '220' => 'S', 844 | ), 845 | 'RSET' => array( 846 | '250' => 'S', 847 | ), 848 | 'QUIT' => array( 849 | '221' => 'S', 850 | '500' => 'E', 851 | '501' => 'E', 852 | '504' => 'E', 853 | ), 854 | ); 855 | } 856 | 857 | class Qdsmtp extends QdsmtpBase{ 858 | function Qdsmtp( $param = null ){ 859 | if( !is_null($param)){ 860 | $param = func_get_args(); 861 | } 862 | parent::QdsmtpBase( $param ); 863 | } 864 | } 865 | //------------------------------------------- 866 | // CakePHP Component 867 | //------------------------------------------- 868 | class QdsmtpComponent extends QdsmtpBase{ 869 | 870 | var $layout = 'default'; 871 | var $view_dir = 'email'; 872 | var $layout_dir = 'email'; 873 | var $template = 'default'; 874 | var $view = null; 875 | 876 | function QdsmtpComponent( $param = null ){ 877 | if( !is_null($param)){ 878 | $param = func_get_args(); 879 | } 880 | parent::QdsmtpBase( $param ); 881 | } 882 | 883 | function startup(&$controller) { 884 | $this->Controller =& $controller; 885 | if( defined( 'COMPONENTS' ) ){ 886 | $this->logFilename(COMPONENTS.$this->name.'.log'); 887 | $this->errorlogFilename( COMPONENTS . '_error' . $this->name . '.log' ); 888 | } 889 | return; 890 | } 891 | } 892 | 893 | --------------------------------------------------------------------------------