├── .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 | [](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 |
--------------------------------------------------------------------------------