<?php
/**
 * Qdmail ver 1.2.6b
 * E-Mail for multibyte charset
 *
 * PHP versions 4 and 5 (PHP4.3 upper)
 *
 * Copyright 2008, Spok in japan , tokyo
 * hal456.net/qdmail    :  http://hal456.net/qdmail/
 * & CPA-LAB/Technical  :  http://www.cpa-lab.com/tech/
 * Licensed under The MIT License License
 *
 * @copyright		Copyright 2008, Spok.
 * @link			http://hal456.net/qdmail/
 * @version			1.2.6b
 * @lastmodified	2008-10-23
 * @license			http://www.opensource.org/licenses/mit-license.php The MIT License
 *
 * Qdmail is sending e-mail library for multibyte language ,
 * easy , quickly , usefull , and you can specify deeply the details.
 * Copyright (C) 2008   spok
 */
//-------------------------------------------
// For CakePHP , extended 'Object' Class ,
// if including in CakePHP Component .
// At normal PHP Script or another Framework ,
// 'QdmailBranch' means Dummy Base Class .
//-------------------------------------------
if (!defined('QD_DS')) {
    define('QD_DS', DIRECTORY_SEPARATOR);
}

if ( defined('CAKE_CORE_INCLUDE_PATH') || defined('CAKE')) {
    class QdmailBranch extends Object{
    }
}else{
    class QdmailBranch{
    }
}

if( !function_exists( 'qd_send_mail' ) ){

    function qd_send_mail( $type , $to = null, $subject = null , $content = null , $other_header = array() , $attach = null, $debug = 0 ){
        $type_org = $type;

        $mail = & Qdmail::getInstance();
        $mail->debug = $debug;
        if(!is_array($type)){
            $type = array('TYPE'=>$type);
        }
        list( $type , $link ) = $mail->keyUpper($type);
        $option = array();
        $return = array();
        $type = array_change_key_case( $type , CASE_UPPER ) ;
        $option = (isset($type['OPTION']) && is_array($type['OPTION'])) ? $type['OPTION'] : array();			$return = (isset($type['RETURN']) && is_array($type['RETURN'])) ? $type['RETURN'] : array();
        if(isset($type['SMTP'])){
            $option = array_merge($option,array('SMTP'=>true,'smtpServer'=>$type['SMTP']));
        }
        $type = isset($type['TYPE']) ? $type['TYPE']:'text';
        $_type=array('TEXT'=>'Text','HTML'=>'Html','DECO'=>'Deco' ,'DECOTEMPLATE'=>'DecoTemplate');
        $easy_method = isset($_type[strtoupper($type)]) ? 'easy'.$_type[strtoupper($type)]:'_';

        if(!method_exists($mail,$easy_method)){
            $mail -> errorGather('Illegal type \''.$type.'\'',__LINE__);
            return false;
        }

        $ret = $mail->{$easy_method}( $to , $subject , $content , $other_header , $attach , $option );

        foreach($return as $method => $value ){
            if(method_exists($mail,$method)){
                $type_org[$link['RETURN']][$method] = $mail -> {$method}($value);
            }
        }
        if(0!==count($return)){
            $type_org[$link['RETURN']]['qd_send_mail'] = $ret;
            $ret = $type_org;
        }

        return $ret;
    }
}

class QdmailBase extends QdmailBranch{

    //----------------------------
    // Default Language
    // If you do not Japanese
    // Please change this propaty for your Language and Encoding
    //----------------------------
    var	$lang_def			= "ja";
    var	$encoding_def		= "utf-8";
    var	$detect_def			= array('ASCII','JIS','UTF-8','EUC-JP','SJIS');
    var $mb_parameter_stack = null;
    var $united_charset		= null;
    var $mime_encode_max	= 75;
    //------------------------
    // Time Zone , Message Id
    //------------------------
    var $time_zone			= null; // '+0900' in Japan
    var $message_id			= true;
    var $salt				= 'qdmail';
    var $message_id_right	= null;
    //----------------------------
    // Line Feed Character & kana
    //----------------------------
    var	$LFC				=  "\r\n";// Notice: CRLF ,If you failed, change to "\n"
    var $LFC_Qmail			=  null;
    var $is_qmail			=  null;
    var $language			= 'ja';
    var $kana				=  false; // kana header
    //----------
    // sysytem
    //----------
    var $kana_content_relation =  false;
    var	$name			= 'Qdmail';
    var	$version		= '1.2.6b';
    var	$xmailer		= 'PHP-Qdmail';
    var $license 		= 'The_MIT_License';
    //--------------------
    // charset , encoding
    //--------------------
    var $charset_header				= 'iso-2022-jp';
    var $charset_content			= 'iso-2022-jp';
    var $charset_attach_filename	= 'iso-2022-jp';
    var $content_transfer_enc_text	= '7bit';
    var $content_transfer_enc_html	= '7bit';
    var $detect_order				= false;
//	var $detect_order				= "iso-2022-jp,eucJP-win,UTF-8,SJIS-win,jis,ASCII";
    var $qdmail_system_charset 		= 'utf-8';
    var	$force_change_charset		= false;
    var $corres_charset				= array(
        'HEADER'=>'charset_header',
        'BODY'=>'charset_content',
        'ATTACH'=>'charset_attach_filename',
        'TEXT'=>'content_transfer_enc_text',
        'HTML'=>'content_transfer_enc_html',
        'DETECT'=>'detect_order',
        'SYSTEM'=>'qdmail_system_charset',
    ) ;
    //--------------------------
    // for address
    //--------------------------
    var $varidate_address_regex	= '/[^@]+@[^@]+/';
    var $allow_blank_header		= false;
    var	$addr_many = array(
        'TO'	=> true,
        'CC'	=> true,
        'BCC'	=> true,
        'FROM'	=> false,
        'REPLYTO'=> false
    );
    var	$addr_head_name = array(
        'TO'	=> 'To',
        'CC'	=> 'Cc',
        'BCC'	=> 'Bcc',
        'FROM'	=> 'From',
        'REPLYTO'=>'Reply-To'
    );
    var $header_must =array( 'TO' , 'FROM' , 'SUBJECT' );
    var $body_empty_allow = false;
    var $tokey		= array(
        '_ADDR' => 'mail',
        '_NAME' => 'name',
    );
    //--------------
    // content_id
    //--------------
    var	$content_id_include_attach_path = false ;
    var	$content_id_only_filename = true ;
    //---------------
    // Once mode
    //---------------
    var $body_structure			= array();
    var $body_build_once		= false;
    var $body_already_build		= false;
    var $attach_build_once		= true;
    var $attach_already_build	= false;
    //------------------------------
    // simple replace
    //------------------------------
    var $simple_replace	= false;
    var $replace		= array();
    var $replace_with_to_priority= true;
    var $replace_def	= array();
    // simple replace command prefix
    var	$rep_prefix		= null;
    //---------
    // wordwrap
    //---------
    var	$wordwrap_allow	= false;
    var	$wrap_prohibit_allow	= false;
    var $wordwrap_length= 45 ;
    // inteligent wordwrap
    // false is that the word exist in the line ,
    // true is that the word must be the beginning of a line
    var	$wrap_except	= array(
        'http://'=>false,
        'code'=>true,
    );
    var $wrap_prohibit_top=',.;:–?!‼、。．)）]}｝〕〉》」』】〙〗〟’”»ヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎ‐〜？！';
    var $wrap_prohibit_end='（([{｛〔〈《「『【〘〖‘“« ';
    var $wrap_prohibit = array();
    // multibyte wordwrap , by wordcount or by wordwidth
    var	$wrap_width	= true;
    // multibyte wordwidth compare by ascii
    var	$mb_strwidth_magni = 2;
    //------------------
    // To Separate mode
    //------------------
    var	$to_separate	= false ;
    //----------------------------
    // html mail
    //----------------------------
    var $is_html		= null ;
    var	$auto_both		= true ; // text & html
    var	$inline_mode	= false;
    var	$deco_kind		= null ; // number of $this->deco_def
    var	$auto_deco_judge= false;
    var $no_inline_attach_structure = 0;
    var $deco_def_default = 0;
    var	$deco_def		=array(
        array(
            'OPTION_NAME'	=> array( 'MHTML' , 'INLINE' , 'PC' ),
            'STRUCTURE'		=> 1,
            '_CHARSET'		=> 'iso-2022-jp' ,
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> false,
            'DISPOSITION'	=> true,
        ),
        array(
            'OPTION_NAME'	=> array( 'DC' , 'DOCOMO' ),
            'STRUCTURE'		=> 2,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> false,
        ),
        array(
            'OPTION_NAME'	=> array( 'AU' ,'EZ', 'EZWEB'),
            'STRUCTURE'		=> 3,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> true,
        ),
        array(
            'OPTION_NAME'	=> array( 'SB' , 'SOFTBANK' ),
            'STRUCTURE'		=> 4,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> array('this','stripCrlf') ,
            'DISPOSITION'	=> true ,
        ),
        array(
            'OPTION_NAME'	=> array( 'EM','EMOBILE' ,'EMNET'),
            'STRUCTURE'		=> 2,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> array('this','stripCrlf') ,
            'DISPOSITION'	=> true ,
        ),
        array(
            'OPTION_NAME'	=> array( 'WL','WILLCOM' ,'POCKET'),
            'STRUCTURE'		=> 2,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> 'QUOTED-PRINTABLE',
            'HTML_EXTERNAL'	=> array('this','stripCrlf') ,
            'DISPOSITION'	=> true ,
        ),
        array(
            'OPTION_NAME'	=> array( 'TEMPLATE_DC' , 'TEMPLATE_DOCOMO' ,'TPL_DC'),
            'STRUCTURE'		=> 5,
            '_CHARSET'		=> 'Shift_JIS',
            'ENC_TEXT'		=> '8bit',
            'ENC_HTML'		=> '8bit',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> false,
            'BOUNDARY'		=> 'mime9DC9bdary',
            'TOP'			=> array('Decomail-Template'),
            'CID_PREFIX'	=> 'img_',
            'CID_NUM_COL'	=> 3,
        ),
        array(
            'OPTION_NAME'	=> array( 'TEMPLATE_AU','TPL_AU' ,'TPL_AU_2_0'),
            'STRUCTURE'		=> 6,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> '7bit',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> false,
            'BOUNDARY'		=> '--=_KDDI_NEXT_PART_0000',
            'TOP'			=> array('KDDI_HTML_MAIL_2_0'),
            'CID_PREFIX'	=> 'img_cid_',
            'CID_NUM_COL'	=> 3,
        ),
        array(
            'OPTION_NAME'	=> array( 'TEMPLATE_AU_1_0','TPL_AU_1_0' ,'TPL_AU_1_0'),
            'STRUCTURE'		=> 6,
            '_CHARSET'		=> 'iso-2022-jp',
            'ENC_TEXT'		=> '7bit',
            'ENC_HTML'		=> '7bit',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> false,
            'BOUNDARY'		=> '--=_KDDI_NEXT_PART_0000',
            'TOP'			=> array('KDDI_HTML_MAIL_1_0'),
            'CID_PREFIX'	=> 'img_cid_',
            'CID_NUM_COL'	=> 3,
        ),
        array(
            'OPTION_NAME'	=> array( 'TEMPLATE_SB','TPL_SB' ),
            'STRUCTURE'		=> 5,
            '_CHARSET'		=> 'Shift_JIS',
            'ENC_TEXT'		=> '8bit',
            'ENC_HTML'		=> '8bit',
            'HTML_EXTERNAL'	=> array('this','stripCrlf'),
            'DISPOSITION'	=> false,
            'BOUNDARY'		=> 'aremejkj15a14',
            'TOP'			=> array('HTMLMail-Template-Version:1.0',
                'HTMLMail-Template-Title:HTMLMail-Template',
                ''
            ),
            'CID_PREFIX'	=> '',
            'CID_NUM_COL'	=> 2,
            'CID_AFTER'		=> '@areme.jp',
        ),
    );

    var		$structure		=array(
        // no inline attachment
        0 => array(
            'multipart/mixed' => array(
                'multipart/alternative'=>array(
                    'html'		=>	1,
                    'plain'		=>	1,
                    'OMIT'		=>	true,
                ),
                'image'		=> 'BOTH', // Available Inline
                'OMIT'		=>	true,
            ),
            'OMIT'		=>	true,
        ),
        // PC inline HTML
        1 => array(
            'multipart/mixed' => array(
                'multipart/alternative'=>array(
                    'multipart/related'	=>	array(
                        'html'		=>	1,
                        'image'		=>	'INLINE',
                        'OMIT'		=>	true,
                    ),
                    'plain'		=>	1,
                    'OMIT'		=>	true,
                ),
                'image'		=> 'NOT_INLINE', // not inline
                'OMIT'	=> true,
            ),
            'OMIT'	=> true,
        ),
        2 => array(
            'multipart/mixed' => array(
                'multipart/related'=>array(
                    'multipart/alternative'	=>	array(
                        'plain'		=>	1,
                        'html'		=>	1,
                        'OMIT'		=>	false,
                    ),
                    'image'		=>	'INLINE',
                    'OMIT'		=>	false,
                ),
                'OMIT'		=>	false,
                'image'		=> 'NOT_INLINE',
            ),
            'OMIT'	=> false,
        ),
        3 => array(
            'multipart/mixed' => array(
                'multipart/alternative'	=>	array(
                    'plain'		=>	1,
                    'html'		=>	1,
                    'OMIT'		=>	false,
                ),
                'image'		=>	'BOTH',
                'OMIT'		=>	false,
            ),
            'OMIT'	=> false,
        ),
        4 => array(
            'multipart/related'=>array(
                'multipart/alternative'	=>	array(
                    'plain'		=>	1,
                    'html'		=>	1,
                    'OMIT'		=>	false,
                ),
                'image'		=>	'INLINE',
                'OMIT'		=>	false,
            ),
            'image'		=> 'NOT_INLINE',
            'OMIT'		=>	false,
        ),
        5 => array(
            'multipart/related'=>array(
                'plain'		=>	1,
                'html'		=>	1,
                'image'		=>	'INLINE',
                'OMIT'		=>	false,
            ),
            'OMIT'		=>	false,
        ),
        6 => array(
            'multipart/mixed'=>array(
                'plain'		=>	1,
                'html'		=>	1,
                'image'		=>	'INLINE',
                'OMIT'		=>	false,
            ),
            'OMIT'		=>	false,
        ),
    );
    var	$deco_judge		= array(
        'docomo.ne.jp'		=> 'DC',
        'softbank.ne.jp'	=> 'SB',
        'i.softbank.ne.jp'	=> 'SB',
        'disney.ne.jp'		=> 'SB',
        'vodafone.ne.jp'	=> 'SB',
        'ezweb.ne.jp'		=> 'AU',
        'emnet.ne.jp'		=> 'EM',
        'pdx.ne.jp'			=> 'WL',
        'gmail.com'			=> 'DC',
    );

    //------------------
    // using address and content
    //------------------
    var	$to				= array()	;
    var	$from			= array()	;
    var	$cc				= array()	;
    var	$bcc			= array()	;
    var	$done			= array()	;
    var	$undone			= array()	;
    var	$replyto		= array()	;
    var	$recipient		= array()	;
    var	$allways_bcc	= null ;
    var	$header			= array()	;
    var	$other_header	= array()	;
    var $header_content_type = array();
    var $content		= array(
        'TEXT'=>array(
            'CONTENT'		=> null,
            'LENGTH'		=> null,
            '_CHARSET'		=> null,
            'ENC'			=> null,
            '_ORG_CHARSET'	=> null,
        ),
        'HTML'=>array(
            'CONTENT'		=> null,
            'ORG_CONTENT'	=> null,
            'LENGTH'		=> null,
            '_CHARSET'		=> null,
            'ENC'			=> null,
            '_ORG_CHARSET'	=> null,
        ),
    );
    var	$header_for_mailfunction_to;
    var	$header_for_mailfunction_subject;
    var	$header_for_mailfunction_other;
    var	$content_for_mailfunction;
    var $header_for_smtp_array;
    var $content_all_for_smtp;
    var	$header_for_smtp;
    //--------------
    // attachament
    //--------------
    var	$attach			= array();
    var	$attach_path	= null;
    var	$auto_ext		= true ; // mimetypes
    var $content_id_fix = false;
    //------------------------
    // Mailer
    //-------------------------
    var $mailer		= 'mail';
    //------------------------
    // Sendmail
    //-------------------------
    var $sendmail = false ;
    var $sendmail_path = null;
    //------------------------
    // SMTP
    //-------------------------
    var $smtp				= false ;
    var $smtp_object		= null;
    var $smtp_loglevel_link	= false;
    var $smtp_server = array(
        'host'		=> null ,
        'port'		=> 25 ,
        'from'		=> null,
        'user'		=> null,
        'pass' 		=> null,
        'protocol'	=> null,
        'pop_host'	=> null,
        'pop_user'	=> null,
        'pop_pass'	=> null,
    );
    //------------------------
    // render Mode
    //------------------------
    var $render_mode		= false;
    var $size			= array();
    //------------------------
    // Priority
    //------------------------
    var $priority			= null;
    var $priority_def   =array(
        'X-Priority'		=> array( 'HIGH' => 1 , 'NORMAL' => 3 , 'LOW' =>5 ),
        'X-MsMail-Priotiry'	=> array( 'HIGH'=>'High' , 'NORMAL'=>'Normal' , 'LOW'=>'Low' ),
        'Priotiry'			=> array( 'HIGH'=>'urgent' , 'NORMAL' => 'normal' , 'LOW'=> 'non-urgent' ),
        'Importance'		=> array( 'HIGH' =>'High' , 'NORMAL'=>'Normal' ,'LOW' =>'Low' ),
    );
    //------------------------
    // signed
    //------------------------
    var $sign						=	false;
    var $smime						=	false;
    var $pgp						=	false;
    var $private_key_file			= 'private.pem';
    var $certificate_file			= 'cert.pem';
    var $certificate_pass			=  null;
    var $certificate_file_path		=  null;
    var $certificate_temp_path		=  null;
    var $certificate_file_path_win	= 'c:\cert';
    var $certificate_temp_path_win	= 'c:\temp';
    var $certificate_file_path_unix	= '/user/local/cert';
    var $certificate_temp_path_unix	= '/tmp';
    //------------------------
    // etc
    //------------------------
    var $temporary_path		= null;
    var $simple_attach		= false;
    var $keep_parameter		= array(false);
    var	$mta_option			= null ;
    var	$is_create			= false;
    var	$address_validation_method  = array('this','validateAddr');
    var	$boundary_base_degit= 2 ;
    var	$stack_construct	= null ;
    var $start_time			= null;
    var $framework			= null;
    //-------------------------------
    // logs
    // 0 is nolog,
    // 1 is simple(Message 'Success' & recipt e-mail@address ),
    // 2 is including header data,
    // 3 is including fulldata,
    //------------------------------
    var	$log_level		= 0 ;
    var	$log_level_max  = 3 ;
    var	$log_path  		= './';
    var	$log_filename	= 'qdmail.log';
    var	$log_append		= 'a' ;
    var	$log_dateformat	= 'Y-m-d H:i:s';
    var	$log_LFC	= "\n";
    // -------------------------------
    // error & error logs
    // 0 is nolog,
    // 1 is simple,
    // 2 is including header data,
    // 3 is inc fulldata
    //--------------------------------
    var $error			= array();
    var $error_stack	= array();
    var $error_display	= true;
    var	$errorlog_level	= 0 ;
    var	$errorlog_level_max = 3 ;
    var	$errorlog_path  = './';
    var	$errorlog_filename= 'qbmail_error.log';
    var	$errorlog_append= 'a' ;
    var	$ignore_error	= false ;
    //----------------
    // debug
    // 0 is no debug mode & really sending ,
    // 1 is showing header&body & really sending ,
    // 2 is no sending & showing header&body and some vars
    //----------------
    var	$debug			= 0 ;
    var	$debug_report	= false;
    var	$debug_report_path = './';
    var	$debug_echo_charset= true;

//****************************************************
//  Methods
//****************************************************
    //--------------------------------
    // constructor   set error display
    // $charset_def = null,
    // $error_display = true
    // $mail -> (&) new Qdmail( Charset , Encoding , DetectOrder , error_display );
    //--------------------------------

    function __construct( $param = null ){
        $this->stack_construct = $param ;
        if( !empty( $param[0] ) && !empty( $param[1] ) ){
            $this->charset( $param[0] , $param[1] );
        }elseif( !empty( $param[0] ) ){
            $this->charset( $param[0] );
        }
        if( !empty( $param[2] ) ){
            $this->detect_order = $param[1];
        }
        if( false !== $this->detect_order ){
            $this->qd_detect_order( $this->detect_order );
        }
        if( !empty( $param[3] ) ){
            $this->error_display = $param[2];
        }

        if(is_null($this->LFC)){
            $this->LFC = chr(13) . chr(10);
        }

        if(is_null($this->LFC_Qmail)){
            $this->LFC_Qmail = chr(10);
        }

        if($this->isQmail()){
            $this->LFC = $this->LFC_Qmail;
        }
        $this->optionNameLink();
        $this->wordwrapProhibitConstruct();
        $this->sendmail_path = ini_get("sendmail_path");
    }

    function & getInstance(){
        static $instance = array();

        if( isset($instance[0]) && is_object($instance[0]) ){
            $keep = $instance[0]->keep_parameter;

            if(  is_string($keep[0]) ){
                $stack = array();
                foreach($keep as $method){
                    if( !is_string( $method ) || !method_exists( $instance[0] , $method ) ){
                        continue;
                    }
                    $stack[$method] = $instance[0]->{$method}();
                }
                $instance[0] -> reset();
                foreach($stack as $method => $value){
                    $instance[0]->{$method}($value);
                }
            }elseif( true !== $keep[0] ){
                $instance[0] -> reset();
            }

            return $instance[0];
        }
        $instance[0] = new Qdmail();
        return  $instance[0];
    }
    //--------------------------
    // Decoration Mail Template
    //--------------------------
    function makeDecoTemplate( $deco_kind , $content ){

        if(false===($this->deco_kind=$this->decoSelect( $deco_kind ))){
            return $this->errorGather('Illegal Decoration Kind \''.$deco_kind.'\'',__LINE__);
        }

        $DECO = new QdDeco;
        $DECO -> template($content);
        $DECO -> decode();
        $content = $DECO -> get('HTML');
        $attach  = $DECO  -> get('ATTACH');
        $this -> renderMode( true );
        $this -> to('dummy@example.com');
        $this -> from('dummy@example.com');
        $this -> subject('dummy_subject');
        $this->body = null;
        $this->after_id = null;
        $this->content_id_fix = true;
        $this->is_html = 'HTML';
        $count = 0;

        $content = $this->qd_convert_encoding($content,'utf-8',$this->qd_detect_encoding($content));
        $content=preg_replace('/\r?\n/','',$content);

        foreach($attach as $key => $att){
            if( empty( $attach[$key]['CONTENT-ID'] ) ){
                continue;
            }

            $aft = isset($this->deco_def[$this->deco_kind]['CID_AFTER']) ? $this->deco_def[$this->deco_kind]['CID_AFTER']:'';
            $prefix = isset($this->deco_def[$this->deco_kind]['CID_PREFIX']) ? $this->deco_def[$this->deco_kind]['CID_PREFIX']:'';
            $col_num = isset($this->deco_def[$this->deco_kind]['CID_NUM_COL']) ? $this->deco_def[$this->deco_kind]['CID_NUM_COL']:3;
            $ct = '00'.$count++;
            $start  = (strlen($ct)-$col_num) < 0 ? 0:strlen($ct)-$col_num;
            $end = strlen($ct)-$start;
            $new_cid =  $prefix
                . substr($ct,$start,$end)
                . $aft;
            $content=preg_replace('/<\s*IMG\s+SRC\s*=\s*"cid:'.$attach[$key]['CONTENT-ID'].'"/is','<IMG SRC="cid:'.$new_cid.'"',$content);
            $attach[$key]['CONTENT-ID'] = $new_cid;
        }
        $this->html( $content , null , null , 'utf-8' );
        if( 0 < count($attach) ){
            $this->attach( $attach );
        }
        $this->createMail(
            $this->deco_def[$this->deco_kind]['BOUNDARY'],
            true
        );

        $header = '';
        foreach($this->deco_def[$this->deco_kind]['TOP'] as $line){
            $header .= $line .$this->LFC;
        }
        $header .= 'MIME-Version: 1.0' . $this->LFC
            . 'Content-type: ' . key($this->structure[$this->deco_def[$this->deco_kind]['STRUCTURE']])
            . '; boundary="'.$this->deco_def[$this->deco_kind]['BOUNDARY'] . '"'
            . $this->LFC;
        return $header . $this->LFC . $this -> smtpDataBody() . $this->LFC ;
    }

    //-------------------
    // Easy Base
    //-------------------
    function easy( $type , $to , $subject , $content , $other_header = array() , $attach = null ){
        if(is_null($other_header)){
            $other_header=array();
        }

        $this->resetHeaderBody();

        $option_return = array();
        if( is_array($type) ){
            $type = array_change_key_case( $type , CASE_UPPER );
            if( isset( $type['SMTP'] ) ){
                $this->smtp( true );
                $this->smtpServer( $type['SMTP'] );
            }
            if(isset( $type['OPTION'] )){
                $type['OPTION'] = array_change_key_case( $type['OPTION'] , CASE_UPPER );
                foreach($type['OPTION'] as $method => $param ){
                    if(method_exists($this,$method)){
                        $option_return[$method] = $this->{$method}($param);
                    }
                }
            }
            $type = isset( $type['TYPE'] ) ? $type['TYPE'] : 'TEXT' ;
        }

        if( (empty($to) && ( !empty($subject) || !empty($content) ))){
            return $this->errorGather('Parameter Specified Error',__LINE__);
        }elseif( empty($to) ){
            return $option_return;
        }

        if( 'TEXT' == strtoupper( $type ) || 'HTML' == strtoupper( $type ) ){
            $type=strtolower( $type );
        }else{
            $this->error[]='Illegal spcify \'type\' in '.$type.' .'.__LINE__;
            return $this->errorGather();
        }
        $to = is_string($to) ? array($to) : $to ;
        $other_header = is_string($other_header) ? array('From' => $other_header) : $other_header ;

        list($other_header_temp , $link ) = $this->keyUpper( $other_header );

        if(!isset($other_header_temp['FROM'])){
            $fromAddr = null;
            if( isset($other_header[0]) ){
                $fromAddr = $other_header[0];
                unset($other_header[0]);
            }
            $fromName = null;
            if(isset($other_header[1])){
                $fromName =  $other_header[1];
                unset($other_header[1]);
            }
            if(!empty($fromAddr)){
                $other_header = array_merge( $other_header,array('FROM'=>array( $fromAddr , $fromName )));
            }
        }

        $other_header = array_merge(array('TO'=>$to),$other_header);
        $section = array('TO'=>'to','CC'=>'cc','BCC'=>'bcc','REPLY-TO'=>'replyto','FROM'=>'from');
        list($other_header_temp , $link ) = $this->keyUpper( $other_header );

        foreach($other_header_temp as $key => $other_head){
            if(isset($section[$key])){
                $method = $section[$key];
                $this -> {$method}( $other_head , null );
            }else{
                $this -> addHeader( $link[$key] ,  $other_head );
            }
        }

        $this->subject( $subject );
        $this->{$type}( $content );
        if( isset( $attach ) ){
            $this->attach( $attach , $add = false , $this->inline_mode );
        }

        return $this->send();
    }

    function easyText( $to , $subject , $content , $other_header = array() , $attach = null , $option = array() ){
        return $this->easy( array('TYPE'=>'text','OPTION'=>$option) , $to , $subject , $content , $other_header ,  $attach );
    }

    function easyHtml( $to , $subject , $content , $other_header = array() , $attach = null , $option = array() ){
        return $this->easy( array('TYPE'=>'html','OPTION'=>$option) , $to , $subject , $content , $other_header ,  $attach );
    }

    function easyReplace( $to , $subject , $content , $other_header = array() , $attach = null , $option = array() ){
        $this->simpleReplace( true );
        $type = 'text';
        if(0!==count($option)){
            $option = array_change_key_case( $option , CASE_UPPER );
            $type = ( 'HTML' === strtoupper( $option['TYPE']) ) ? 'html' : $type ;
        }
        $this->easy( array('TYPE'=>$type,'OPTION'=>$option) , $to , $subject , $content , $other_header ,  $attach );
    }

    function easyDeco( $to , $subject , $content , $other_header = array() , $attach = null , $option = array() ){
        if( isset( $attach ) ){
            $this->inline_mode=true;
        }
        $this->autoDecoJudge( true );
        $this->toSeparate( true );

        return $this->easy( array('TYPE'=>'html','OPTION'=>$option) , $to , $subject , $content , $other_header ,  $attach );
    }
    function easyDecoTemplate( $to , $subject , $template , $other_header = array() , $attach = null , $option = array() ){
        if(is_null($attach)){
            $attach = array();
        }
        $DECO = new QdDeco;
        $DECO -> template($template);
        $DECO -> decode();
        $content = $DECO -> get('HTML');
        $text = $DECO -> get('PLAIN');
        if(!empty($text)){
            $this->text($text);
        }
        $att = $DECO ->get('ATTACH');
        $attach = array_merge($att,$attach);
        return $this->easyDeco( $to , $subject , $content , $other_header , $attach , $option );
    }

    function easyDecoRep( $to , $subject , $content , $other_header = array() , $attach = null , $option = array() ){
        $this->simpleReplace( true );
        return $this->easyDeco($to , $subject , $content , $attach , $option , $option);
    }

    function easyOption( $to , $subject = null , $content = null , $other_header = array() , $attach = null , $option = array() ){

        if(!is_array($to)){
            $option = array( $to => $subject );
        }else{
            $option = $to;
        }
        return $this->easy( array('TYPE'=>'option','OPTION'=>$option) , $to , $subject , $content , $other_header ,  $attach );
    }

    /*
 * Notice: Before use $this->optionNameLink(); by Constractor
*/
    //
    //---------------------------------------
    // something change mode
    //---------------------------------------
    // Keys must lowercase , because of PHP4's
    var	$property_type = array(
        'auto_both'			=> 'bool' ,
        'to_separate'		=> 'bool' ,
        'simple_replace'	=> 'bool' ,
        'auto_deco_judge'	=> 'bool' ,
        'auto_ext'			=> 'bool' ,
        'body_empty_allow'	=> 'bool' ,
        'ignore_error'		=> 'bool' ,
        'wrap_width'		=> 'bool' ,
        'wordwrap_allow'	=> 'bool' ,
        'wrap_prohibit_allow'=> 'bool' ,
        'force_change_charset'	=> 'bool' ,
        'error_display'		=> 'bool' ,
        'sendmail'			=> 'bool' ,
        'smtp'				=> 'bool' ,
        'smtp_loglevel_link'=> 'bool' ,
        'inline_mode'		=> 'bool' ,
        'replace_with_to_priority'=> 'bool' ,
        'attach_build_once'	=> 'bool' ,
        'body_build_once'	=> 'bool' ,
        'kana'				=> 'bool' ,
        'render_mode'		=> 'bool' ,
        'smime'				=> 'bool' ,
        'pgp'				=> 'bool' ,
        'simple_attach'		=> 'bool' ,
        'message_id'		=> 'bool' ,
        'allow_blank_header'=> 'bool' ,
        'sign'				=> 'string' ,
        'keep_parameter'	=> 'array' ,
        'attach_path'		=> 'string' ,
        'mta_option'		=> 'string' ,
        'rep_prefix'		=> 'string' ,
        'log_path'			=> 'string' ,
        'errorlog_path'		=> 'string' ,
        'log_filename'		=> 'string' ,
        'errorlog_filename'	=> 'string' ,
        'allways_bcc'		=> 'string' ,
        'wrap_prohibit_top'	=> 'string' ,
        'wrap_prohibit_end'	=> 'string' ,
        'framework'			=> 'string' ,
        'priority'			=> 'string' ,
        'certificate_file'	=> 'string' ,
        'certificate_file_path'	=> 'string' ,
        'certificate_temp_path'	=> 'string' ,
        'time_zone'			=> 'string' ,
        'private_key_file'	=> 'string' ,
        'certificate_pass'	=> 'string' ,
        'message_id_right'	=> 'string' ,
        'sendmail_path'		=> 'string' ,
        'temporary_path'	=> 'string' ,
        'united_charset'	=> 'string' ,
        'varidate_address_regex'=> 'string' ,
        'mb_strwidth_magni'	=> 'numeric' ,
        'log_dateformat'	=> 'numeric' ,
        'log_level'			=> 'numeric' ,
        'errorlog_level'	=> 'numeric' ,
        'mime_encode_max'	=> 'numeric' ,
        'smtp_server'			=> 'array' ,
        'address_validation_method'=> 'array',
    );
    var	$method_property	= array();

    function optionNameLink(){
        foreach($this->property_type as $prop => $type ){
            $method_low = strtolower( str_replace( '_' , '' , $prop ) );
            $this->method_property[$method_low] = $prop;
        }
    }
    function option( $option , $line = null , $min = null , $max = null ){
        $ret = array();
        if( !is_null( $line ) ){
            $line = '-' . $line ;
        }
        if(!is_array($option)){
            return $this->errorSpecify( __FUNCTION__, __LINE__ );
        }
        foreach( $option as $key => $value ){
            if( !isset( $this->method_property[strtolower($key)] ) ){
                return $this->errorSpecify( __FUNCTION__ . '-' .$key , __LINE__ . $line );
            }
            $property_name = $this->method_property[strtolower($key)];
            if( is_null( $value ) ){
                $ret[] = $this->{$property_name} ;
                continue ;
            }
            $err = false;
            switch( $this->property_type[$property_name] ){
                case 'bool':
                    if( is_bool( $value ) ){
                        $this->{$property_name} = $value ;
                        $ret[0] = true ;
                    }else{
                        return $this->errorSpecify( __FUNCTION__ . '-' .$key , __LINE__ . $line );
                    }
                    break;
                case 'string':
                    if( '' === $value ){
                        $this->{$property_name} = null ;
                        $ret[0] = true ;
                        break ;
                    }
                    if( is_string( $value ) ){
                        $this->{$property_name} = $value ;
                        $ret[0] = true ;
                    }else{
                        return $this->errorSpecify( __FUNCTION__ . '-' .$key , __LINE__ . $line );
                    }
                    break;
                case 'numeric':
                    if( !is_numeric( $value ) || ( isset( $min ) && ( $value < $min ) ) || ( isset( $max ) && ( $value > $max ) ) ){
                        return $this->errorSpecify( __FUNCTION__ . '-' .$key , __LINE__ . $line );
                    }else{
                        $this->{$property_name} = $value ;

                        $ret[0] = true ;
                    }
                    break;
                case 'array':
                    if( !is_array( $value ) ){
                        $value = array( $value );
                    }
                    if( true===$min ){
                        $this->{$property_name} = array_merge( $this->{$property_name} , $value );
                    }else{
                        $this->{$property_name} = $value ;
                    }
                    $ret[0] = true ;
                    if( true === $max ){
                        $this->{$property_name} = array_change_key_case( $this->{$property_name} , CASE_UPPER );
                    }
                    break;

                default:
                    return $this->errorSpecify( __FUNCTION__ . '-' .$key , __LINE__ . $line );
                    break;
            }
        }
        if( 0 === count( $ret ) ){
            return $this->errorSpecify( __FUNCTION__ , __LINE__ );
        }elseif( 1 === count( $ret ) ){
            return array_shift( $ret );
        }else{
            return $ret ;
        }
    }

    function autoBoth( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function toSeparate( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function simpleReplace( $bool = null ){
        $this->toSeparate( $bool );
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function autoDecoJudge( $bool = null ){
        $this->attachBuildOnce( !$bool );
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function autoExt( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function bodyEmptyAllow( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function ignoreError( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function forceChangeCharset( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function errorDisplay( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function sendmail( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function smtp( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function smtpLoglevelLink( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function inlineMode( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function replaceWithToPriority( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function attachBuildOnce( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function bodyBuildOnce( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function kana( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function keepParameter( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function renderMode( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function simpleAttach( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function messageId( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function allowBlankHeader( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function smime( $bool = null ){
        $fg = $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
        if(!extension_loaded ( 'openssl' )){
            $this->smime = false;
            if(!$bool){
                return false;
            }
        }
        return $fg;
    }
    function pgp( $bool = null ){
        // future
        return true;
    }
    function sign( $string = null ){
        if(false===$string){
            $this->smime = false;
            $this->pgp   = false;
            $this->sign  = false;
            return true;
        }
        if(empty($string)){
            return $this->sign;
        }
        $string = strtoupper($string);
        if('S/MIME'==$string){
            $this->smime(true);
        }elseif('PGP'==$string){
            $this->pgp(true);
        }else{
            return false;
        }
        $fg = $this->option( array( __FUNCTION__ => $string ) ,__LINE__);
        return $fg;
    }
    function size( $kind = null ){

        if(empty($this->header_for_smtp)){
            $stack = $this->render_mode;
            $this->render_mode = true;
            $fg = $this->send();
            $this->render_mode = $stack;
        }

        $this->size['ALL'] = strlen( bin2hex( $this->header_for_smtp . $this->LFC . $this->content_for_mailfunction )) / 2;
        $this->size['HEADER'] = strlen( bin2hex( $this->header_for_smtp )) / 2;
        $this->size['BODY'] = strlen( bin2hex( $this->content_for_mailfunction )) / 2;

        if(is_null($kind)){
            return $this->size;
        }
        $kind = strtoupper( $kind );
        if(isset($this->size[$kind])){
            return $this->size[$kind];
        }
        return false;
    }
    function sizeAll(){
        return $this->size('ALL');
    }
    function sizeHeader(){
        return $this->size('HEADER');
    }
    function sizeBody(){
        return $this->size('BODY');
    }
    function smtpData(){
        if(empty($this->header_for_smtp)){
            $stack = $this->render_mode;
            $this->render_mode = true;
            $fg = $this->send();
            $this->render_mode = $stack;
        }
        return  $this->header_for_smtp . $this->LFC . $this->content_for_mailfunction ;
    }
    function smtpDataBody(){
        if(empty($this->content_for_mailfunction)){
            $stack = $this->render_mode;
            $this->render_mode = true;
            $fg = $this->send();
            $this->render_mode = $stack;
        }
        return  $this->content_for_mailfunction ;
    }
    function isQmail(){
        if(!is_null($this->is_qmail)){
            return $this->is_qmail;
        }
        $this->is_qmail = false;
        $ret = ini_get ( 'sendmail_path' );
        if(false !== strpos($ret,'qmail')){
            $this->is_qmail = true;
        }
        $sendmail_path = ini_get('sendmail_path');
        if(false !== @system($sendmail_path.' -d0.1 < /dev/null > /dev/null',$ret)){
            if(is_array($ret)){
                $ret = reset($ret);
            }
            $code = (int) substr($ret,0,3);
            if( 100 === $code || 111 === $code){
                $this->is_qmail = true;
            }
        }

        return $this->is_qmail ;
    }
    function lineFeed( $LFC = null ){
        if(is_null($LFC)){
            return $this->LFC;
        }
        if(preg_match('/[\r|\n|\r\n]/is',$LFC)){
            $this->LFC = $LFC;
            return true;
        }else{
            return false;
        }
    }
    function isWin(){
        return false!==strpos(PHP_OS,'WIN');
    }
    //---------------------------------------
    // something change mode
    //---------------------------------------
    function whichTextHtml( $which ){
        $which = strtoupper( $which );
        if( 'TEXT' == $which ){
            $this->is_html='TEXT';
        }elseif( 'HTML' == $which ){
            $this->is_html='HTML';
        }elseif( 'BOTH' == $which ){
            $this->is_html='BOTH';
        }
    }

    function allwaysBcc( $option = null ){
        if( is_null( $option ) ){
            return $this->allways_bcc ;
        }
        if( $this->option( array( __FUNCTION__ => $option ) ,__LINE__) ){
            $fg = $this->extractAddr( $this->allways_bcc ) ;
        }
        if( $this->errorGather() && $fg && !empty($this->allways_bcc) ){
            return true ;
        }else{
            $this->allways_bcc = array();
            return false ;
        }
    }
    function priority( $option = null ){
        $fg=$this->option( array( __FUNCTION__ => $option ) ,__LINE__);
        $priority = strtoupper($option);
        if(empty($priority)){
            return $fg;
        }
        $kind = array('HIGH'=>1,'NORMAL'=>1,'LOW'=>1);
        if( !isset( $kind[$priority] ) ){
            return $this->errorGather('Illegal Priority Name \''.$option.'\'',__LINE__);
        }
        foreach($this->priority_def as $header_name => $values){
            $this->addHeader($header_name,$values[$priority]);
        }
        return $this->errorGather();
    }
    function certificatePass( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function certificateFilePath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function certificateTempPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function certificateFile( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function privateKeyFile( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function framework( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function attachPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function timeZone( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function messageIdRight( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function mtaOption( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function unitedCharset( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function varidateAddressRegex( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function sendmailPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function temporaryPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function logPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function errorlogPath( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function logDateFormat( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function logFilename( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function errorlogFilename( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function logLevel( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__, 0 , $this->log_level_max );
    }
    function errorlogLevel( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__, 0 , $this->errorlog_level_max );
    }
    function mimeEncodeMax( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
    }
    function smtpServer( $array = null ){
        return $this->option( array( __FUNCTION__ => $array ) ,__LINE__, true , true );
    }
    function addressValidationMethod( $array = null ){
        return $this->option( array( __FUNCTION__ => $array ) ,__LINE__, false , true );
    }
    //------------------
    //version
    //------------------
    function version(){
        return $this->version;
    }
    //------------------
    //mb_ wrapper
    //------------------
    function qd_detect_encoding( $word ){
        if(!is_null($this->united_charset)){
            return $this->united_charset;
        }else{
            return mb_detect_encoding( $word , mb_detect_order() , true );
        }
    }
    function qd_convert_encoding( $word , $target_chrset , $org_charset = null ){

        if(empty($org_charset)){
            $org_charset = $this->qd_detect_encoding( $word );
        }
        if(empty($org_charset)){
            return $word;
        }
        if( strtoupper( $target_chrset ) === strtoupper( $org_charset ) ){
            return $word;
        }
        if('ASCII'===strtoupper( $target_chrset ) || 'ASCII'===strtoupper( $org_charset )){
            return $word;
        }
        return mb_convert_encoding( $word , $target_chrset , $org_charset );
    }
    function qd_detect_order( $param=null ){
        if(is_null($param)){
            return mb_detect_order();
        }else{
            return mb_detect_order( $param );
        }
    }
    //-----------------------------------------
    // Address and Name Keys change Opiton
    //-----------------------------------------
    function addressField( $addr = null , $name = null ){
        if( is_null($addr) && is_null($name) ){
            return array( $this->tokey['_ADDR'] , $this->tokey['_NAME'] );
        }
        if( !is_null($addr) && is_array($addr) && 1 < count($addr) ){
            $_addr = array_shift( $addr ) ;
            $name = array_shift( $addr ) ;
            $addr = $_addr;
        }
        if( (!is_null($addr) && !is_string( $addr )) || !is_null($name) && !is_string($name) ){
            return $this->errorGather('Specify Error in addressField',__LINE__);
        }
        $addr = is_null( $addr ) ? $this->tokey['_ADDR'] : $addr ;
        $name = is_null( $name ) ? $this->tokey['_NAME'] : $name ;
        $this->tokey = array(
            '_ADDR' => $addr,
            '_NAME' => $name,
        );
        return true;
    }
    //-----------------------------------------------------------
    // Wordwrap Opiton
    // array( 'except word' => beginning flag )
    // if beginning flag is true , beginning of a line is target
    // if beginning flag is true , the word in line is target
    //-----------------------------------------------------------
    function wordwrapProhibitConstruct(){
        $ret = $this->strToArrayKey( $this->wrap_prohibit_top , true );
        $ret2 = $this->strToArrayKey( $this->wrap_prohibit_end , false );
        $this->wrap_prohibit = array_merge( $ret , $ret2 );
    }
    function strToArrayKey( $word , $value ){
        $ret = array();
        $enc = $this->qd_detect_encoding( $word );
        $length = mb_strlen( $word , $enc );
        for( $i=0 ; $i < $length ; $i++ ){
            $ret[ mb_substr( $word , $i , 1 , $enc ) ] = $value;
        }
        return $ret;
    }
    function wordwrapAllow( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function wrapProhibitAllow( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function wrapProhibitEnd( $option = null ){
        $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
        $this->wordwrapProhibitConstruct();
        return  $this->errorGather();
    }
    function wrapProhibitTop( $option = null ){
        $this->option( array( __FUNCTION__ => $option ) ,__LINE__);
        $this->wordwrapProhibitConstruct();
        return  $this->errorGather();
    }
    function wrapWidth( $bool = null ){
        return $this->option( array( __FUNCTION__ => $bool ) ,__LINE__);
    }
    function wordwrapLength( $length = null ){
        if( is_null( $length ) ){
            return $this->wordwrap_length;
        }
        if( !is_numeric( $length ) || ( is_numeric( $length ) &&  $length < 1 ) ){
            return $this->errorGather('Wordwrap error , length is illegal' ,__LINE__) ;
        }
        $this->wordwrap_length = $length;
        return $this->errorGather() ;
    }
    function mbStrwidthMagni( $option = null ){
        return $this->option( array( __FUNCTION__ => $option ) ,__LINE__, 0 , 10 );
    }
    function wrapExcept( $array = null ){
        if( null === $array ){
            return $this->wrap_except;
        }
        if( is_string( $array ) || is_numeric( $array ) ){
            $this->wrap_except = array( $array =>false );//default false
        }
        if( is_array( $array ) ){
            if( 0 === count( $array ) ){
                $this->wrap_except = array();
                return $this->errorGather() ;
            }
            foreach( $array as $key => $value){
                if( !is_bool( $value ) ){
                    $array[$key] = empty( $value ) ? false:true;
                }else{
                    $array[$key] = $value ;
                }
            }
            $this->wrap_except = $array;
            return $this->errorGather() ;
        }
        return $this->errorSpecify(__FUNCTION__,__LINE__);
    }
    //------------------------------------------
    // Charset Option
    //------------------------------------------
    function charsetHeader( $charset = null ){
        if(is_null($charset)){
            return $this->charset_header ;
        }
        $stack = $this->charset();
        $stack['HEADER'] = $charset;
        return $this->charset( $stack );
    }
    function charsetAttach( $charset = null ){
        if(is_null($charset)){
            return $this->charset_attach_filename ;
        }
        $stack = $this->charset();
        $stack['HEADER'] = $charset;
        return $this->charset( $stack );
    }
    function charsetBody( $charset = null , $enc = null ){
        if( is_null($charset) && is_null($enc) ){
            return array($this->charset_content , $this->content_transfer_enc_text , content_transfer_enc_html) ;
        }
        if(is_array($charset)){
            $enc = isset($charset[1]) ? $charset[1]:null;
            $charset = $charset[0];
        }
        $stack = $this->charset();
        if( !is_null($charset) ){
            $stack['BODY'] = $charset;
        }
        if( !is_null($enc) ){
            $stack['HTML'] = $stack['TEXT'] = $enc;
        }
        return $this->charset( $stack );
    }
    function charset( $array = null , $op = null ){
        if( is_null( $array ) && is_null( $op ) ){
            foreach( $this->corres_charset as $key => $value ){
                $ret[$key] = $this->{$value} ;
            }
            return $ret;
        }
        if( !is_null($op) && is_string($op) ){

            $this->content_transfer_enc_text = $op ;
            $this->content_transfer_enc_html = $op ;

            return $this->charset( $array );
        }elseif(!is_null($op) && !is_string($op)){
            return $this->errorSpecify( __FUNCTION__ , __LINE__ );
        }
        if( is_array( $array ) ){
            if( 2===count($array) && isset($array[0]) && isset($array[1])){
                return $this->charset($array[0],$array[1]);
            }
            $array = array_change_key_case( $array , CASE_UPPER );
            foreach( $array as $key => $value ){
                if(isset($this->corres_charset[$key])){
                    if( is_string($this->corres_charset[$key]) ){
                        $this->{$this->corres_charset[$key]} = $value;
                    }else{
                        return $this->errorSpecify( __FUNCTION__ , __LINE__ );
                    }
                }
            }
        }elseif( is_string($array) ){
            $this->charset_header = $this->charset_content = $this->charset_attach_filename = $array;
        }else{
            return $this->errorSpecify( __FUNCTION__ , __LINE__ );
        }
        return $this->errorGather() ;
    }
    function encoding( $enc = null ){
        if(is_null($enc)){
            return $this->corres_charset['TEXT'];
        }
        $this->corres_charset['TEXT'] = $this->corres_charset['HTML'] = $enc;
        return true;
    }
    //--------------------------
    // set Mutibye Parameter
    //--------------------------
    function setMbParameter( $lang = null , $internal_enc = null , $detect = null ){

        if(is_array($lang)){
            mb_language( $lang[0] ) ;
            mb_internal_encoding( $lang[1] ) ;
            $this->qd_detect_order( $lang[2] );
        }elseif( 'STACK'===strtoupper($lang) ){
            $this->mb_parameter_stack = array(mb_language(),mb_internal_encoding(),$this->qd_detect_order());
            mb_language( $this->lang_def );
            mb_internal_encoding( $this->encoding_def );
            $this->qd_detect_order( $this->detect_def );
        }else{
            if( !is_null( $lang ) ){
                mb_language( $lang ) ;
            }
            if( !is_null( $internal_enc ) ){
                mb_internal_encoding( $internal_enc ) ;
            }
            if( !is_null( $detect ) ){
                $this->qd_detect_order( $detect );
            }
        }
    }
    //--------------------------------
    // Decorationable HTML Mail Opiton
    // ( Inline HTML , MHTML )
    // See $this->deco_def Property
    //--------------------------------
    // Change decoration default along to each career
    function decoDef( $value = null ){
        if( is_null( $value ) ){
            return $this->deco_def_default;
        }
        $this->deco_def_default = $value ;
        return $this->errorGather() ;
    }
    // fix Decoration Pattern by STRING means CareerName
    function decoFix( $cari = null ){
        if(is_null($cari)){
            return $this->deco_kind;
        }
        $this->deco_kind = $this->decoSelect( $cari );
        return true;
    }
    function decoSelect( $deco_kind = null ){
        if( is_null( $deco_kind ) ){
            return $this->deco_def_default;//$this->deco_judge
        }
        $deco_kind = strtoupper( $deco_kind );
        $ret = false ;
        foreach( $this->deco_def as $key => $def ){
            if( in_array( $deco_kind , $def['OPTION_NAME'] ) ){
                $ret = $key ;
                $this->inline_mode = true;
            }
        }
        return $ret;
    }

    // Change Decoration Pattern by E-mail Address
    function decoJudge( $addr_array ){
        $addr=$addr_array[$this->tokey['_ADDR']];
        $start = strrpos( $addr , '@');
        if(empty($start)){
            return $this->deco_def_default;
        }
        $right = trim(substr($addr , $start+1));
        $parts = explode('.',$right);
        $ct = count($parts);
        if( 2 > $ct ){
            return $this->deco_def_default;
        }
        $domains = array();
        $domains[] =  $parts[$ct-2] . '.' . $parts[$ct-1];
        if( isset($parts[$ct-3]) ){
            $domains[] =  $parts[$ct-3] .'.'.$parts[$ct-2] . '.' . $parts[$ct-1];
        }
        if( isset($parts[$ct-3]) && isset($parts[$ct-4]) ){
            $domains[] =  $parts[$ct-4] .'.'. $parts[$ct-3] .'.'.$parts[$ct-2] . '.' . $parts[$ct-1];
        }
        $ct = count($domains);
        $domain = reset($domains);
        while( $ct-- > 0){
            if(isset( $this->deco_judge[$domains[$ct]])){
                $domain = $domains[$ct];
                break;
            }
        }
        return $this->decoSelect(isset( $this->deco_judge[$domain] ) ? $this->deco_judge[$domain]:null);
    }
    //------------------------------------
    //
    // Word Replace
    //
    // You can add prefix by $this->rep_prefix proparty
    // OR $this->repPrefix() Method (Recommended)
    // notice: this functino need on utf-8
    // OR $this->qdmail_system_charset need utf-8
    //------------------------------------
    function replaceWord( $array = null , $add = false ){
        if( is_null( $array ) ){
            return $this->replace ;
        }
        if( !is_array( $array ) ){
            $array = array( $array );
        }
        foreach($array as $key => $arr){
            if( !is_array( $arr ) ){
                $array[$key] = array( $arr );
            }
        }
        if( $add ){
            $this->replace = array_merge( $this->replace , $array );
        }else{
            $this->replace = $array ;
        }
        return $this->errorGather() ;
    }
    function replaceDef( $array = null ){
        if(is_null($array)){
            return $this->replace_def;
        }
        if(is_array($array)){
            $this->replace_def = $array;
        }else{
            return false;
        }
    }

    function repPrefix( $option = null ){
        return $this->stringOption( __FUNCTION__ , $option , __LINE__ );
    }
    function replace( $cont , $rep ){
        foreach($rep as $serch => $replace ){
            if( '_' == mb_substr( $serch , 0 , 1 , $this->qd_detect_encoding($serch) ) ){
                continue;
            }
            if( empty($replace) && !empty($this->replace_def[$serch]) ){
                $replace = $this->replace_def[$serch];
            }
            $reg = '/%' . $this->rep_prefix . (string) $serch . '%/is' ;
            $cont = $this->qdmail_preg_replace( $reg , $replace , $cont );
        }
        return $cont;
    }
    function qdmail_preg_replace( $reg , $rep , $cont ){
        $enc = $this->qd_detect_encoding( $cont );
        $_reg = $this->qd_convert_encoding( $reg , $this->qdmail_system_charset , $this->qd_detect_encoding( $reg ) );
        $_rep = $this->qd_convert_encoding( $rep , $this->qdmail_system_charset , $this->qd_detect_encoding( $rep ) );
        $_cont = $this->qd_convert_encoding( $cont , $this->qdmail_system_charset , $enc );
        $cont = preg_replace( $_reg , $_rep , $_cont );
        return $this->qd_convert_encoding($cont , $enc , $this->qdmail_system_charset );
    }
    //------------------------------------
    // OOP User Interface (Recommended)
    //------------------------------------
    function to( $addr = null , $name = null , $add = false ){
        return $this->addrs( 'TO' , $addr , $name , $add );
    }
    function cc( $addr = null  , $name = null , $add = false ){
        return $this->addrs( 'CC' , $addr , $name , $add );
    }
    function bcc( $addr = null  , $name = null , $add = false ){
        return $this->addrs( 'BCC' , $addr , $name , $add );
    }

    function from( $addr = null  , $name = null ){
        return $this->addrs( 'FROM' , $addr , $name , false );
    }
    function replyto( $addr = null  , $name = null ){
        return $this->addrs( 'REPLYTO' , $addr , $name , false );
    }
    function addHeader( $header_name = null , $value = null){
        if('REPLY-TO'==strtoupper($header_name)){
            $header_name = 'REPLYTO' ;
        }
        if(isset($this->addr_head_name[strtoupper($header_name)])){
            return $this->{strtolower($header_name)}( $value , null , true );
        }		if(is_null($header_name)){
            return $this->other_header;
        }
        if('clear'===strtolower($header_name) && is_null($value)){
            $this->other_header=array();
            return ;
        }
        $this->other_header[$header_name] = $value ;
    }
    function reset( $debugErase = false ){

        if( !$debugErase ){
            $stack_debug = $this->debug ;
        }

        $stack = $this->stack_construct;
        $array = get_class_vars( $this->name ) ;
        foreach($array as $key => $value){
            $this->{$key} = $value ;
        }

        $this->__construct( $stack );
        if( !$debugErase ){
            $this->debug = $stack_debug ;
        }
    }
    function resetHeader(){
        $this->to = array();
        $this->cc = array();
        $this->bcc = array();
        $this->from = array();
        $this->replyto = array();
        $this->other_header=array();
        $this->subject = null;
    }
    function resetBody(){
        $this->body('');
        $this->is_html = null;
        $this->deco_kind = null;
        $this->inline_mode = false;
        $this->attach  = array();
    }
    function resetHeaderBody(){
        $this->resetBody();
        $this->resetHeader();
    }

    function _gatherFromArray( $array , $key ){
        $ret = array();
        foreach( $array as $ar ){
            $ret[] = $ar[$key] ;
        }
        return $ret;
    }
    function done(){
        return $this->_gatherFromArray( $this->done , $this->tokey['_ADDR'] );
    }
    function undone(){
        return $this->_gatherFromArray( $this->undone , $this->tokey['_ADDR'] );
    }

    function subject( $subj = null ){
        if( is_null($subj) ){
            return $this->subject;
        }

        if( is_string( $subj ) || is_numeric( $subj ) ){
            $this->subject['CONTENT'] = (string) $subj;
            return $this->errorGather() ;
        }elseif( is_array($subj) ){
            $subj = array_change_key_case( $subj , CASE_UPPER );
            if(isset($subj['CONTENT'])){
                $this->subject = $subj;
            }else{
                $this->subject['CONTENT'] = (string) $subj[0];
                $this->subject['_CHARSET'] = isset($subj[1]) ? $subj[1] : null ;
                $this->subject['_ORG_CHARSET'] = isset($subj[2]) ? $subj[2] : null ;
            }
            return $this->errorGather() ;
        }else{
            return $this->errorSpecify(__FUNCTION__,__LINE__);
        }
    }

    function body( $type =null , $cont = null , $length = null , $charset = null , $enc = null , $org_charset = null ){
        if(is_null($type)){
            return $this->content;
        }
        if(empty($type)){
            $this->content		= array(
                'TEXT'=>array(
                    'CONTENT'		=> null,
                    'LENGTH'		=> null,
                    '_CHARSET'		=> null,
                    'ENC'			=> null,
                    '_ORG_CHARSET'	=> null,
                ),
                'HTML'=>array(
                    'CONTENT'		=> null,
                    'ORG_CONTENT'	=> null,
                    'LENGTH'		=> null,
                    '_CHARSET'		=> null,
                    'ENC'			=> null,
                    '_ORG_CHARSET'	=> null,
                ),
            );
            return true;
        }
        $type = strtolower( $type );
        if( 'text'!==$type && 'html'!==$type ){
            return $this->errorGather('You must use \'text\' or \'html\'' ,__LINE__) ;
        }
        if( is_array( $cont ) ){
            $def = array(
                'CONTENT'=>null,
                'LENGTH'=>null,
                '_CHARSET'=>null,
                'ENC'=>null,
                '_ORG_CHARSET'=>null,
            );
            $temp = array_change_key_case( array_merge($def,$cont) , CASE_UPPER);
        }else{
            $temp = array(
                'CONTENT'=>$cont,
                'LENGTH'=>$length,
                '_CHARSET'=>$charset,
                'ENC'=>$enc,
                '_ORG_CHARSET'=>$org_charset,
            );
        }
        $this->content[strtoupper($type)] = array_merge( $this->content[strtoupper($type)] , $temp );
        return $this->errorGather() ;
    }

    function text( $cont , $length = null , $charset = null , $enc = null , $org_charset = null ){
        return $this->body('text', $cont , $length , $charset , $enc , $org_charset );
    }

    function html( $cont , $charset = null , $enc = null , $org_charset = null ){
        return $this->body('html', $cont , null , $charset , $enc , $org_charset );
    }
    //--------------------------
    // assist User Interface
    //--------------------------
    function addrs( $section , $addr = null  , $name = null , $add = false ){
        $section = strtolower( $section );
        $ck = array('to'=>true,'from'=>true,'cc'=>true,'bcc'=>true,'replyto'=>true);
        if(empty($ck[$section])){
            return $this->errorGather('Illegal Section Name \''.$section.'\'' ,__LINE__) ;
        }
        if( is_null( $addr ) && is_null( $name )){
            return  $this->{$section} ;
        }
        if( false === $addr ){
            $this->{$section} = array();
            return $this->errorGather() ;
        }
        $addr = $this->analyzeAddr( $addr , $name );

        if( !$this->allow_blank_header && empty($addr[0][$this->tokey['_ADDR']]) ){// if addres is empty , no set
            return true;
        }

        if( !$add ){
            $this->{$section} = $addr;
        }else{
            $this->{$section} = array_merge( $this->{$section} , $addr );
        }

        return ( 0 != count( $addr ) );
    }

    function analyzeAddr( $addr , $name ){
        if( is_string( $addr ) ){
            if( empty( $name ) ){
                list( $name , $addr ) = $this->_extractNameAddr( $addr );
            }else{
                $addr = $this->extractAddr( $addr );
            }
            return array(array( $this->tokey['_ADDR'] => $addr , $this->tokey['_NAME'] => $name ));
        }
        // $addr is array
##		list( $addr , $void ) = $this->keyUpper( $addr );
        $ret = array();
        if( empty( $name ) || !is_array( $name ) ){
            if(isset($addr[$this->tokey['_ADDR']])){
                $addr[$this->tokey['_NAME']] = isset($addr[$this->tokey['_NAME']]) ? $addr[$this->tokey['_NAME']]:null;
                return array( $addr );//ver 0.7.3a
            }elseif( isset( $addr[0] ) && is_array( $addr[0] ) ){
                foreach($addr as $ad){
##					list( $ad , $void ) = $this->keyUpper( $ad );
                    $_addr = isset( $ad[$this->tokey['_ADDR']] ) ?  $this->extractAddr( $ad[$this->tokey['_ADDR']] ) : $this->extractAddr( $ad[0] ) ;
                    if(isset( $ad[$this->tokey['_NAME']] ) ){
                        $_name = $ad[$this->tokey['_NAME']];
                    }elseif( isset( $ad[1] ) ){
                        $_name = $ad[1];
                    }else{
                        $_name = null;
                    }
                    if( empty($_addr) ){
                        continue;
                    }else{
                        $ret[] = array_merge( $ad , array( $this->tokey['_ADDR'] => $_addr , $this->tokey['_NAME'] => $_name ) );
                    }
                }
                return $ret;
            }else{
                $_addr = $this->extractAddr( $addr[0] );
                $_name = isset($addr[1]) ? $addr[1]:null;
                $ret[] = array($this->tokey['_ADDR'] => $_addr , $this->tokey['_NAME'] => $_name);
            }
            return $ret; //fool proof
        }else{
            foreach( $addr as $key => $value ){
                $_addr = $this->extractAddr( $value );
                $_name = $name[$key] ;
                if( empty( $_addr ) ){
                    continue;
                }else{
                    $ret[] = array( $this->tokey['_ADDR'] => $_addr , $this->tokey['_NAME'] => $_name );
                }
            }
            return $ret;
        }
        return $ret; // fool proof
    }

    //--------------------------------------------------------
    // From MutibyteName<example@example.com> To MutibyteName
    //--------------------------------------------------------
    function _extractNameAddr( $addr ){
        $formed_addr = $this->extractAddr( $addr );
        if( empty( $formed_addr ) ){
            return false;
        }
        $addr = trim($addr);
        $addr = str_replace(array('<','>'),'',$addr);
        $temp=strpos($addr,$formed_addr);
        if( false === $temp ){
            return null;
        }
        return array( substr( $addr , 0 , strpos( $addr , $formed_addr )) , $formed_addr );
    }

    function setContentArgs( $type , $param ){
        $method_name = 'text';
        if('HTML' == $type ){
            $method_name = 'html';
        }
        $cont = null;
        if(isset($param[$type])){
            $cont = $param[$type];
        }elseif(isset($param['CONTENT'])){
            $cont = $param['CONTENT'];
        }

        return $this->{$method_name}(
            $cont ,
            isset($param['_CHARSET']) ? $param['_CHARSET']:null,
            isset($param['ENC']) ? $param['ENC']:null,
            isset($param['_ORG_CHARSET']) ? $param['_ORG_CHARSET']:null
        );

    }
    //-------------------------------------------
    // Main Routine Send()
    //   Option analyize
    //   Is To-Separate Mode ?
    //     loop:sendbase
    //       Already Created Mail?
    //       Create mail
    //         Additional Parameter(From User) Analyize (e.g. charset , subject etc...)
    //           (Not OOP MODE)
    //         Build Header(except Content-type etc) and Must Header Checking
    //           Both mode ? text only or html only or both ? or auto both
    //           Addition Attachment will do
    //           Select Body Structure by Decoration Pattern or else
    //         Build Body ( Recursive )
    //         Render Body with 'Content-type' Header and Boundary etc..
    //           +  finalize( Recursive )
    //              Pass to the Header,first Content-type etc. that needs by Header Render Routine
    //         Set Default Header, MIME 1.0 etc
    //         Render Header and Render for SMTP Sender Text(Future)
    //   Debug Echo & log & error log will do if you want
    //If error exsist , no sender(except ignore_error Property)
    //-------------------------------------------
    function headerDefault(){
        $this->header['MIME-Version'] = '1.0';
        if($this->debug > 0 ){
            $this->header['X-QdmailDebug'] = trim(chunk_split ( base64_encode($this->iniGather()) , $this->mime_encode_max ,  $this->LFC."\t" ));
        }
        $this->header['X-'.$this->xmailer] = trim('version-'.$this->version . ' ' . $this->license .' http://hal456.net/qdmail PHPver '.PHP_VERSION);
        if($this->smtp){
            $sendby = 'SMTP';
        }elseif($this->sendmail && !ini_get('SafeMode')){
            $sendby = 'Sendmail';
        }elseif($this->sendmail && ini_get('SafeMode')){
            $sendby = 'MailFunction but Sendmail if no Safemode';
        }else{
            $sendby = 'MailFunction';
        }
        $this->header['X-'.$this->xmailer] .= $this->LFC . chr(9) . 'send-by '.$sendby;
    }
    function makeMessageId(){
        $req_uri = empty($_SERVER['REQUEST_URI']) ? '':$_SERVER['REQUEST_URI'];
        if(is_null($this->message_id_right)){
            $right = 'hal456.net';
        }else{
            $right = $this->message_id_right;
        }
        $id = 'Qdmail.' . $this->version
            . '_' . sha1( microtime() . $this->salt . mt_rand() . $req_uri )
            . '@' . $right ;
        return '<'.$id.'>';
    }

    function send( $option = null ){
        if( is_null( $this->start_time )){
            $this->start_time = microtime();
        }

        // mb language
        if( 'neutral' === mb_language() ){
            $this->setMbParameter('stack');
        }

        if( is_object( $option ) ){
            $this->smtp_object = & $option;
            $this->smtp = true;
            $option = null ;
        }
        // Date: header
        if( !is_null($this->time_zone) ){
            $other = array_change_key_case($this->other_header,CASE_UPPER);
            if( !isset($other['DATE']) ){
                $this->other_header['Date'] =  date('D, d M Y h:i:s ') . $this->time_zone;
            }
        }

        $fg = true;
        if( true === $this->toSeparate() ){
            $stack_tos = array( $this->to , $this->cc , $this->bcc );
            $tos = $this->to ;
#			$this->cc( false ) ;
#			$this->bcc( false ) ;
            if( empty( $tos ) ){
                $fg = $this->errorGather('recipient Header is not exsit line' ,__LINE__) ;
            }else{
                // To Separate mode
                foreach($tos as $key => $to){
                    if( $this->simple_replace ){
                        if($this->replace_with_to_priority){
                            $to = array_merge( $this->selectReplace( $to , $key ) ,$to );
                        }else{
                            $to = array_merge( $to , $this->selectReplace( $to , $key )  );
                        }
                    }
                    $this->to( $to , null , false );


                    $this->debugEchoLf($this->to);

                    if( $this->auto_deco_judge ){
                        $this->deco_kind = $this->decoJudge( $this->to[0] );
                    }
                    if( $this->sendBase() ){
                        $this->is_create = false; // for next to
                        continue ;
                    }else{
                        $this->is_create = false; // for next to
                        $fg = $this->errorGather('Error \'TO\' Separate mode in Sendbase function , the Address is -> '.$this->to[0][$this->tokey['_ADDR']] ,__LINE__) ;
                    }
                }
            }
            list( $this->to , $this->cc , $this->bcc ) = $stack_tos ;

        }else{
            // normal mode the end
            $fg = $this->sendBase() ;
            $this->is_create = false;
        }

        $this->setMbParameter($this->mb_parameter_stack);
        $this->log();
        //debug
        $this->debugEcho('END');
        if( $fg ){
            return $this->errorGather();
        }else{
            return $this->errorGather('Send Error' ,__LINE__) ;
        }
    }

    function selectReplace( $to , $key ){
        $ret = array();
        if( isset( $this->replace[$to[$this->tokey['_ADDR']]] ) ){
            $ret = $this->replace[$to[$this->tokey['_ADDR']]];
        }elseif( isset( $this->replace[$key] ) ){
            $ret = $this->replace[$key];
        }
        return $ret ;
    }

    function sendBase(){
        // stack bcc for allways bcc
        unset( $stack_bcc ) ;
        if( 0 != count( $this->allways_bcc ) ){
            $stack_bcc = $this->bcc ;
            $this->bcc( $this->allways_bcc , null , true );
        }
        // Message Id
        if( $this->message_id){
            $other = array_change_key_case($this->other_header,CASE_UPPER);
            if(!isset($other['MESSAGE-ID'])){
                $this->other_header['Message-Id'] =  $this->makeMessageId();
            }
        }
        if( !$this->is_create ){
            $this->body = null;
            $this->after_id = null;
            $this->createMail();
        }
        if( isset($option) && !empty($option) ){
            list( $option , $void ) = $this->keyUpper( $option );
        }
        // for smtp and sendmail
        $this->extractrecipient() ;
        $fg = true;
        $fg_debug = ( 2 > $this->debug ) && !$this->render_mode;
        if( $fg_debug && (  ( 0 === count( $this->error ) ) && ( 0 === count( $this->error_stack ) ) ) || $this->ignore_error ) {
            //
            //  mail or SMTP or sendmail
            //
            if( $this->smtp ){
                $fg = $this->sendBySmtp();
            }elseif( $this->sendmail &&  !ini_get('safe_mode') ){
                $fg = $this->sendBySendmail();
            }elseif( ini_get('safe_mode') ){
                $fg = mail(
                    trim( $this->header_for_mailfunction_to )
                    , trim( $this->header_for_mailfunction_subject )
                    , $this->content_for_mailfunction
                    , trim( $this->header_for_mailfunction_other )
                );
            }else{

                $fg = mail(
                    trim( $this->header_for_mailfunction_to )
                    , trim( $this->header_for_mailfunction_subject )
                    , $this->content_for_mailfunction
                    , trim( $this->header_for_mailfunction_other )
                    , trim( $this->mta_option )
                );

            }

            if( $fg ){
                $this->done = array_merge( $this->done , $this->to , $this->cc , $this->bcc ) ;
            }else{
                $this->undone = array_merge( $this->undone , $this->to , $this->cc , $this->bcc ) ;

                $err_mes = $this->smtp ? 'SMTP mail method':'PHP mail function';
                $err_mes = $this->sendmail ? 'sendmail of localhost':$err_mes;

                $fg =$this->errorGather('No send . Because '.$err_mes.' replied error' ,__LINE__);
            }
        }elseif( $fg_debug ){
            $this->undone = array_merge( $this->undone , $this->to , $this->cc , $this->bcc ) ;
            $fg = $this->errorGather('Error happen, see upper' ,__LINE__);;
        }else{
            $this->undone = array_merge( $this->undone , $this->to , $this->cc , $this->bcc ) ;
            $fg = true ;
        }
        //debug
        $bcc = null;
        if( !empty($this->header_for_smtp_bcc )){
            $bcc = '('.$this->header_for_smtp_bcc.')';
        }
        $this->debugEchoLf(
            $bcc ,
            $this->content_all_for_smtp,
//			$this->header_for_smtp,
//			$this->content_for_mailfunction,
            $this->LFC.$this->LFC ,
            date('Y-m-d H:i:s')
        );
        if($this->debug_report){
            $this->debugReport('FILE');
        }

        if( isset( $stack_bcc ) ){
            $this->bcc = $stack_bcc ;
        }
        return $this->errorGather() && $fg;
    }
    //-----------------
    // checking
    //-----------------
    function mustCheck(){
        if( 0 == count( $this->header_must ) ){
            return $this->errorGather() ;
        }
        $must = true;
        foreach( $this->header_must as $hdn ){
            $header_upp = array_change_key_case( $this->header , CASE_UPPER );
            if( ( !$this->smtp && empty( $header_upp[strtoupper($hdn)] ) ) || ( $this->smtp && empty( $header_upp[strtoupper($hdn)] ) && !isset( $this->smtp_server['FROM'] ) ) ){
                $must = $this->errorGather('Must Header is not exist \''.$hdn.'\'' ,__LINE__) ;
            }
        }
        return $must;
    }
    //-----------------------------------------------
    //  Create one mail
    //-----------------------------------------------
    function createMail( $boundary = null , $boundary_fix = false){

        $this->_charsetDefFix();
        //
        // content(body) force convert to utf-8 ,
        // because some system function can't do collectlly whitout utf-8,ex preg_replace,striptags
        //
        $this->content = $this->convertCharsetRecursive( $this->content , $this->qdmail_system_charset );
        $this->buildHeader();
        if(!$this->mustCheck()){
            return false;
        };

        // Text only or Html Only or both ?
        if( empty( $this->is_html ) ){
            if( $this->issetContent( $this->content['HTML'] ) && $this->issetContent( $this->content['TEXT'] ) ){
                $this->is_html = 'BOTH' ;
            }elseif( $this->issetContent( $this->content['HTML'] ) && $this->auto_both ){
                $this->content['TEXT'] = array(
                    'content'=>$this->htmlToText( $this->content['HTML']['CONTENT'] )
                );
                $this->is_html = 'BOTH';
            }elseif( $this->issetContent( $this->content['HTML'] ) && !$this->auto_both ){
                $this->is_html = 'HTML';
            }else{
                $this->is_html = 'TEXT';
            }
        }
        // Select Body Structure
        if( !isset( $this->deco_kind ) ){
            $structure_no = 0 ;
        }else{
            $structure_no = $this->deco_def[$this->deco_kind]['STRUCTURE'];
        }

        // Short cut on many recipients , samebody
        if( !$this->body_build_once && $this->attach_build_once && $this->attach_already_build){
            //only text and html making
            $this->replaceBodyStructure('TEXT');
            $this->replaceBodyStructure('HTML');
        }elseif(
            ($this->body_build_once && !$this->body_already_build)
            ||
            ( !$this->body_build_once && ( !$this->attach_build_once || ( $this->attach_build_once && !$this->attach_already_build ) ) )
        ){
            $this->body_structure = $this->buildBody( $this->structure[$structure_no] ,$boundary, false , $boundary_fix );
        }
        if( !$this->body_build_once || ($this->body_build_once && !$this->body_already_build) ){
            $this->renderBody();//including Content-type Header
        }

        $this->header = array_merge($this->header , $this->header_content_type);
        // user added header
        $this->headerDefault();
        $this->renderHeader();
        //
        // signed
        //
        if( false===$this->sign ){
            $this->content_all_for_smtp = $this->header_for_smtp . $this->LFC . $this->content_for_mailfunction;
        }elseif($this->pgp){
            // future PGP
        }else{
            // S/MIME
            $this->content_all_for_smtp = $this->signSmime();
            if(false===$this->content_all_for_smtp){
                return $this->errorGather('Sign Error S/MIME',__LINE__);
            }
        }
        $this->is_create=true;
    }

    function signSmime(){
        if( !$this->smtp ){
            return $this->errorGather('S/MIME needs SMTP Send,now You spcify no smtp',__LINE__);
        }
        // Path to certificate file , by Win or other OS
        if(empty($this->certificate_file_path)){
            $this->certificate_file_path = $this->isWin() ? $this->certificate_file_path_win : $this->certificate_file_path_unix;
        }
        if(empty($this->certificate_temp_path)){
            $this->certificate_file_path = $this->isWin() ? $this->certificate_file_path_win : $this->certificate_file_path_unix;
        }
        $path = $this->certificate_file_path . QD_DS ;

        if('PFX'===strtoupper(substr($this->certificate_file,-3))){
            if(!function_exists('openssl_pkcs12_read')){
                return $this->errorGather('You can not specify *.pfx type, please *.pem type becuase your PHP Version do not support \'openssl_pkcs12_read\'',__LINE__);
            }
            if(openssl_pkcs12_read(file_get_contents($path.$this->certificate_file),$ret,$this->certificate_pass)){
                $private_key = $ret['pkey'];
                $certificate = $ret['cert'];
            }else{
                return $this->errorGather('Illegal Certificate File  \''.$path.$this->certificate_file.'\' or Incorrect Password ',__LINE__);
            }
        }else{
            $private_key = file_get_contents( $path . $this->private_key_file );
            $certificate = file_get_contents( $path . $this->certificate_file );
        }
        $temp = sha1($this->content_for_mailfunction).'.txt';
        $temp_filename = $this->certificate_temp_path.QD_DS.'temp'.$temp;
        $temp_signed_filename = $this->certificate_temp_path.QD_DS.'temp_signed'.$temp;

        $fp = fopen( $temp_filename , "w" );
        fputs( $fp , $this->content_for_mailfunction . $this->LFC );
        fclose($fp);
        unset($this->header_for_smtp_array['MIME-Version']);

        openssl_pkcs7_sign(
            $temp_filename,
            $temp_signed_filename,
            $certificate,
            array($private_key, $this->certificate_pass),
            $this->header_for_smtp_array
        );
        $ret = file_get_contents($temp_signed_filename);
        unlink ( $temp_signed_filename );
        unlink ( $temp_filename );
        return  $ret;
    }

    function replaceBodyStructure( $kind ){
        $content_type = ( 'TEXT' === $kind ) ? 'text/plain':'text/html';
        $false = false;
        $rep = & $this->serchBodyStructure( $content_type , $this->body_structure , $false );
        if( false === $rep ){
            return false;
        }
        list( $content , $charset , $enc ) = $this->makeContentText( $this->content[$kind] , $kind );
        $rep['CONTENT'] = $content;
        $rep['HEADER']['Content-Type'] = $content_type.'; charset="' . $charset . '"';
        $rep['HEADER']['Content-Transfer-Encoding'] = $enc ;
    }

    function & serchBodyStructure( $content_type , & $bbs , & $false ){
        foreach($bbs as $fkey => $bs){
            if( isset( $bs['HEADER']) && ( 0 < count($bs['HEADER']))) {
                $len = strlen($content_type);
                foreach( $bs['HEADER'] as $key => $cont ){
                    if( ('CONTENT-TYPE' === strtoupper($key)) && ($content_type === substr($cont,0,$len)) ){
                        return $bbs[$fkey];
                    }
                }
            }
            if( !isset( $bs['CONTENT']) || ( isset( $bs['CONTENT']) && !is_array( $bs['CONTENT']))){
                continue;
            }
            $ret = & $this->serchBodyStructure( $content_type , $bbs[$fkey]['CONTENT'] , $false );
            return $ret;
        }
        return $false;
    }
    //except Content-type,user option
    function buildHeader(){
        $header = array();
        foreach( $this->addr_many as $section => $many ){
            if( 0 == count( $this->{strtolower( $section )} ) ){
                continue;
            }
            foreach( $this->{strtolower($section)} as $one ){
                $mime=$this->mimeEncode(
                    $one[$this->tokey['_NAME']],
                    isset($one['_CHARSET']) ? $one['_CHARSET'] : $this->charset_header,
                    isset($one['_ORG_CHARSET']) ? $one['_ORG_CHARSET'] : null,
                    strlen($section)+2
                );
                // bcc header is not allowed MimeName
                if( empty( $mime ) || 'BCC'===strtoupper( $section ) ){
                    $header[$this->addr_head_name[$section]][] = $one[$this->tokey['_ADDR']];
                }else{
                    $header[$this->addr_head_name[$section]][] = $mime.' <'.$one[$this->tokey['_ADDR']].'>';
                }
            }
            if( !$many ){
                $header[$this->addr_head_name[$section]] = array( array_pop( $header[$this->addr_head_name[$section]] ) );
            }
        }
        if( !empty( $this->subject ) ){
            //replace
            if( $this->simple_replace ){
                $subj = $this->replace( $this->subject['CONTENT'] , $this->to[0] );
            }else{
                $subj = $this->subject['CONTENT'] ;
            }
            $header['Subject']=$this->mimeEncode(
                $subj ,
                isset($this->subject['_CHARSET']) ? $this->subject['_CHARSET']:$this->charset_header,
                isset($this->subject['_ORG_CHARSET']) ? $this->subject['_ORG_CHARSET'] : null,
                9 //strlen(subject)+2
            );
        }
        $this->header = array_merge( $header , $this->other_header ) ;
    }
    function renderHeader(){
        if(isset($this->header['To'])){
            $this->header_for_mailfunction_to = implode( ','.$this->LFC.' ' , $this->header['To'] );
            unset( $this->header['To'] ) ;
        }
        if(isset($this->header['Subject'])){
            $this->header_for_mailfunction_subject = $this->header['Subject'];
            unset( $this->header['Subject'] ) ;
        }

        $this->header_for_mailfunction_other = null;
        $header_for_smtp = array();
        $this->header_for_smtp_bcc = null;

        $header_for_smtp['To'] = $this->header_for_mailfunction_to;
        $header_for_smtp['Subject'] = $this->header_for_mailfunction_subject;
        foreach( $this->header as $key => $value ){
            if( is_array( $value ) ){
                $add = implode( ',' . $this->LFC . chr(9) , $value );
            }else{
                $add = $value;
            }
            if( 'BCC' !== strtoupper($key) ){
                $header_for_smtp[$key] =  $add ;
            }else{
                $this->header_for_smtp_bcc = $key . ': ' . $add . $this->LFC ;
            }
            $this->header_for_mailfunction_other .= $key . ': ' . $add . $this->LFC;
            unset( $this->header[$key] );
        }

        $this->header_for_smtp = '';

        foreach($header_for_smtp as $key => $value){
            $this->header_for_smtp .= $key.': '.$value.$this->LFC;
        }

        if($this->smime){
            $this->header_for_smtp_array = $header_for_smtp;
        }
    }

    //-------------------------
    // $ret = array(
    //     'BOUNDARY' =>
    //     'HEADER' =>
    //     'CONTENT' =>array(
    //			(Recursive)
    //		)
    //	)
    //-------------------------
    function isInlineImage($filename){
        if(!empty($this->content['HTML']['ORG_CONTENT'])){
            $cont = $this->content['HTML']['ORG_CONTENT'];
        }
        if(!empty($this->content['HTML']['CONTENT'])){
            $cont = $this->content['HTML']['CONTENT'];
        }
        if(empty($cont)){
            return false;
        }
        $enc = $this->qd_detect_encoding($cont);
        $cont = $this->qd_convert_encoding($cont,'UTF-8',$enc);
        $name = $filename;
        $enc = $this->qd_detect_encoding($name);
        $name = $this->qd_convert_encoding($filename,'utf-8',$enc);
        if( 0 < preg_match('/"cid:'.$name.'"/is' , $cont ) ){
            return true;
        }else{
            return false;
        };
    }
    function buildBody( $structure , $boundary = null , $rel = false , $boundary_fix = false){
        $ret = array();
        $one = array();
        if( is_null( $boundary ) ){
            $boundary = $this->makeBoundary();
        }
        $ret_boundary = $boundary ;
        foreach($this->attach as $key => $att){
            if($this->isInlineImage(basename($this->attach[$key]['PATH'])) && empty($this->attach[$key]['CONTENT-ID'])){
                $this->attach[$key]['CONTENT-ID'] = basename($this->attach[$key]['PATH']);
            }
        }

        foreach( $structure as $key => $value ){

            $ret_header = array();
            $ret_cont = array();
            if( is_array( $value ) ){
                $next_boundary = $boundary_fix ? $boundary:$this->makeBoundary();
                $ret_header['Content-Type'] = strtolower($key).';' . $this->LFC
                    . ' boundary="' . $next_boundary . '"' ;
                $rel = false;
                $ret_cont = $this->buildBody( $value , $next_boundary , $rel);
                if( 0 == count($ret_cont) && $structure['OMIT']){
                    continue;
                }elseif( 1 == count($ret_cont) && $structure['OMIT']){
                    $one = null;
                    $ret_cont[0]['BOUNDARY'] = '--'.$boundary ;
                    $ret[] = $ret_cont[0];
                    continue;
                }else{
                    $one = null;
                    $ret_cont[] = array( 'BOUNDARY' => null ,'HEADER' => array() ,'CONTENT' => '--' . $next_boundary . '--' );
                    $ret[] = array( 'BOUNDARY' => '--' . $ret_boundary , 'HEADER' => $ret_header , 'CONTENT' => $ret_cont );
                    continue;
                }
            }else{
                switch( strtolower($key) ){
                    case 'image':
                        foreach( $this->attach as $att){
                            if( ( 'INLINE' === $value ) && $this->isSetCid( $att ) ){
                                $ret_cont[]= $this->buildAttach( $att , $boundary , true ) ;
                            }elseif( ( 'NOT_INLINE' === $value ) && !$this->isSetCid( $att )){
                                $ret_cont[] = $this->buildAttach( $att , $boundary , false ) ;
                            }if(  'BOTH' === $value  ){
                                $ret_cont[] = $this->buildAttach( $att , $boundary , $this->isSetCid( $att ) ) ;
                            }
                        }
                        break;
                    case 'html':
                        $this->content['ORG_CONTENT'] = $this->content['HTML'];
                        list( $content , $charset , $enc ) = $this->makeContentText( $this->content['HTML'] , 'HTML' );
                        $ret_header['Content-Type'] = 'text/html; charset="' . $charset . '"';
                        $ret_header['Content-Transfer-Encoding'] = $enc ;
                        $ret_cont = $content ;

                        break;
                    case 'plain':
                        list( $content , $charset , $enc ) = $this->makeContentText( $this->content['TEXT'] , 'TEXT' );
                        $ret_header['Content-Type'] = 'text/plain; charset="' . $charset . '"';
                        $ret_header['Content-Transfer-Encoding'] = $enc ;
                        $ret_cont = $content ;
                        break;
                    case 'omit':
                        $one = null;
                        break;
                }
                if( !empty($ret_cont) ){
                    $ret[] = array( 'BOUNDARY' => '--' . $boundary , 'HEADER' => $ret_header , 'CONTENT' => $ret_cont );
                }
            }
        }
        return $ret ;
    }

    function renderBody(){

        if( ( 0 === count( $this->body_structure ) ) && !$this->body_empty_allow ){
            return $this->errorGather('Empty Body do not allowed. If you want to send empty Mail , use method -> bodyEmptyAllow(true)' ,__LINE__) ;

        }elseif( 0 === count( $this->body_structure ) ){
            $this->body_structure[0]['HEADER'] = array();
        }

        if( !$this->smime ){
            foreach( $this->body_structure[0]['HEADER'] as $key => $value){
                $this->header_content_type[$key]=$value;
            }
            $this->body_structure[0]['HEADER'] = array();
        }

        $this->body_structure[0]['BOUNDARY'] = null;

        $this->content_for_mailfunction = rtrim($this->finalize( $this->body_structure )) ;
        $this->body_already_build = true ;
    }

    function finalize( $array ){
        foreach( $array as $ar ){
            $header = $this->expandHeader( $ar['HEADER'] );
            $bd = isset($ar['BOUNDARY']) ? trim($ar['BOUNDARY']) . $this->LFC : null ;
            if(is_array($ar['CONTENT'])){
                if( !empty( $header ) ){
                    $this->body =  $this->body . $bd . $header . $this->LFC . $this->LFC ;
                }
                $this->finalize( $ar['CONTENT'] );
            }else{
                if( !empty( $header ) ){
                    $header .= $this->LFC . $this->LFC ;
                }
                $add = $bd . $header .  $ar['CONTENT']  ;
                $this->body =  $this->body . $add . $this->LFC . $this->LFC ;
            }
        }
        return $this->body;
    }
    function expandHeader( $hds ){
        if(empty($hds)){
            return null;
        }
        $header = null;
        foreach( $hds as $key => $value ){
            if( isset( $value ) ){
                $header .= $key . ': ' . $value . $this->LFC;
            }
        }
        return trim($header);
    }
    function makeBoundary(){
        static $rec = 0 ;
        $boundary = '__Next-' . $rec . '-' . $this->qdmail_md( null , 65 , 90 ) . 'UWRtYWlsIEFHUEx2Mw==' . base64_encode( $this->qdmail_md() ) . '__';
        $rec ++ ;
        return $boundary;
    }
    function makeContentText( $content , $is_text = 'TEXT' ){
        $flag_wrp = ( 'TEXT' == $is_text ) ? true:false;
        $enc = ( 'HTML' == $is_text ) ? $this->content_transfer_enc_html : $this->content_transfer_enc_text ;
        if( is_array( $content ) ){
            $content = array_change_key_case( $content , CASE_UPPER );
            $_content = $content['CONTENT'];
            $org_char = $this->qdmail_system_charset ; //already converted to system charaset
            $target_char = isset($content['_CHARSET'])
                ? $content['_CHARSET'] : $this->charset_content;
            $length = isset($content['LENGTH'])
                ? $content['LENGTH'] : $this->wordwrap_length;
            $content_transfer_enc = !empty($content['ENC'])
                ? $content['ENC'] : $enc;
            $content = $_content;

        }else{
            $org_char = $this->qdmail_system_charset ;
            $target_char = $this->charset_content;
            $length = $this->wordwrap_length;
            $content_transfer_enc = $enc;
        }
        // fix crlf
        list($content,$void) = $this->clean($content);
        // Content_replace
        if( $this->simple_replace ){
            $content = $this->replace( $content , $this->to[0] );
        }
        // Content-id replace
        if(!$this->content_id_fix){
            $content = $this->replaceCid( $content );
        }

        // content modify by external function at HTML
        if( 'HTML' == $is_text && isset($this->deco_kind) && isset($this->deco_def[$this->deco_kind]['HTML_EXTERNAL']) ){
            $temp = $this->deco_def[$this->deco_kind]['HTML_EXTERNAL'];
            if( is_array( $temp ) && 'this'==$temp[0]){
                $content = $this->{$temp[1]}($content);
            }elseif( !empty( $temp ) ){
                $content = call_user_func( array($temp[0],$temp[1]) , $content);
            }
        }
        if( $this->wordwrap_allow && $flag_wrp && false !== $length ){
            $content = $this->mbWordwrap( $content , $length );
        }

        $enc_upp = strtoupper($content_transfer_enc);
        if( $this->kana && 'ja'===$this->language && (('BASE64' === $enc_upp && $this->kana_content_relation) || 'BASE64' !== $enc_upp )){
            $content = mb_convert_kana( $content , 'KV' , $org_char );
        }

        $content = $this->qd_convert_encoding( $content , $target_char , $org_char );
        if( 'BASE64' == $enc_upp && !empty( $content ) ){
            $content = chunk_split( base64_encode( $content ) );
        }elseif( ( 'QUOTED-PRINTABLE' == $enc_upp || 'QP' == $enc_upp ) && !empty( $content ) ){
            $content_transfer_enc = 'quoted-printable';
            $content = $this->quotedPrintableEncode( $content );
        }
        return array( $content , $target_char , $content_transfer_enc );
    }
    //--------------
    // html => text
    // must utf-8 because of preg_replace & strip_tags function
    //--------------
    function htmlToText( $html ){
        $_content = str_replace( array( "\r" , "\n" ) , '' , $html );
        $_content = preg_replace( array( '/<br>/i','/<\/p>/i' , '/<br\s*\/>/i' , '/<\/div>/i' , '/<\/h[1-9]>/i' , '/<\/ol>/i' , '/<\/dl>/i' , '/<\/ul>/i' , '/<li>/i' , '/<\/li>/i' , '/<\/dd>/i' , '/<\/blockquote>/i' , '/<hr\s*\/?>/i' , '/<\/tr>/i' , '/<\/caption>/i' ), $this->LFC , $_content );
        $_content = preg_replace( array( '/<\/td>/i' , '/<\/th>/i' ), ' ' , $_content );
        $_content = preg_replace( "/\\r?\\n/", "\n" , $_content );
        $_content = preg_replace( "/[\\n]+/", "\n" , $_content );
        $_content = preg_replace( "/\\n/", $this->LFC , $_content );
        return trim(strip_tags($_content));
    }

    function mimeEncode( $subject , $charset , $org_charset = null , $first_line_front_words_length = 12 ) {

        $enc = isset($org_charset) ? $org_charset:$this->qd_detect_encoding($subject);
        if( empty($subject) || ( strlen(bin2hex($subject))/2 == mb_strlen($subject,$enc) ) ){
            return trim(chunk_split($subject, $this->mime_encode_max, "\r\n "));
        }
        if($this->kana && 'ja'===$this->language){
            $subject = mb_convert_kana( $subject , 'KV' , $enc );
        }
        $subject = $this->qd_convert_encoding( $subject , $charset , $enc );
        $start = "=?" . $charset . "?B?";
        $end = "?=";
        $spacer = $end . $this->LFC . chr(9) . $start;

        $length = $this->mime_encode_max - strlen($start) - strlen($end);

        $pointer = 1;
        $cut_start = 0;
        $line = null;
        $_ret = array();
        $max = mb_strlen( $subject ,$charset );
        while( $pointer <= $max ){
            $line  = mb_substr( $subject , $cut_start , $pointer-$cut_start , $charset );
            $bs64len = strlen(bin2hex(base64_encode($line)))/2;
            if( (0!==count($_ret) && $bs64len <= $length) || (0===count($_ret) && $bs64len <= ($length-$first_line_front_words_length)) ){
                $pointer ++;
            }else{
                $_ret[] = base64_encode($line) ;
                $cut_start = $pointer;
            }
        }
        if( strlen( trim( $line ) ) > 0){
            $_ret[] = base64_encode( $line );
        }
        $ret = $start . implode( $spacer , $_ret ) . $end;
        $ret = preg_replace(array('/\0/is','/\r[^\n]/is'),'',$ret);
        return $ret ;
    }

    function extractAddr($addr_including_sclub){
        if( preg_match( '/<([^>]+)>/' , $addr_including_sclub , $match ) == 0){
            $addr = $addr_including_sclub;
        }else{
            $addr = $match[1];
        }

        $temp = $this->address_validation_method;
        if( is_array( $temp ) && 'this'==$temp[0]){
            $fg = $this->{$temp[1]}( $addr );
            $mess ="System";
        }elseif( !empty( $temp ) ){
            $fg = call_user_func( array($temp[0],$temp[1]) , $addr );
            $mess ="USER";
        }

        if( $fg ){
            return  $addr ;
        }else{
            return $this->errorGather('Illegal Mail Address'.$mess.'Validete Address Method' ,__LINE__) ;
        }
    }
    //----------------------------------------------------------------
    // Charset ReDecear - if Decoration Pattern needs anather charset
    //  (Overload)
    //----------------------------------------------------------------
    function _charsetDefFix(){
        if( !isset( $this->deco_kind ) ){
            return ;
        }
        if(isset($this->deco_def[$this->deco_kind]['_CHARSET'])){
            $this->charset_content = $this->deco_def[$this->deco_kind]['_CHARSET'];
        }
        if(isset($this->deco_def[$this->deco_kind]['ENC_TEXT'])){
            $this->content_transfer_enc_text = $this->deco_def[$this->deco_kind]['ENC_TEXT'];
        }
        if(isset($this->deco_def[$this->deco_kind]['ENC_HTML'])){
            $this->content_transfer_enc_html = $this->deco_def[$this->deco_kind]['ENC_HTML'];
        }
    }

    //------------------------------------------------------------
    // Addition Header(in send( param ) ) set to $this->{to} etc.
    // and return UpperCase keys
    //------------------------------------------------------------
    function setAddr( $header ){

        if( empty( $header ) ){
            return array( $header , array() );
        }
        list( $header , $link_hd )= $this->keyUpper( $header );
        foreach( $this->addr_many as $section => $void){
            if( !isset( $header[strtoupper($section)] ) ){
                continue;
            }
            $this->{strtolower( $section )}( $header[strtoupper($section)] , null , true );
            unset( $header[strtoupper( $section )] );
        }
        // TO Separate mode?
        if( true === $this->toSeparate() ){
            $this->cc(false);
            $this->bcc(false);
        }
        return array( $header , $link_hd );
    }

    function convertCharsetRecursive( $array , $target_enc ){

        if( is_array( $array ) && !empty( $array['_ORG_CHARSET'] ) ){
            foreach($array as $key => $value){
                if( false === strpos( $key , '_CHARSET' ) ){
                    $array[$key] = $this->qd_convert_encoding($value , $target_enc ,$array['_ORG_CHARSET']  );
                }
            }
        }elseif( is_string( $array ) || is_numeric( $array ) ){
            $enc = $this->qd_detect_encoding( $array );
            $array = $this->qd_convert_encoding($array , $target_enc , $enc );
        }elseif( is_array( $array ) ){
            foreach( $array as $key => $value ){
                $ret[$key] = $this->convertCharsetRecursive( $value , $target_enc );
            }
            $array = $ret ;
        }elseif( empty( $array ) ){
            $array = null ;
        }else{
            $this->error[]='Error convertCharsetRecursive, invalid type ,line->'.__LINE__;
        }
        return $array ;
    }
    function extractrecipient(){
        $hd = array('to','cc','bcc') ;
        $ret = array();
        foreach( $hd as $hdn ){
            foreach($this->{$hdn} as $addr ){
                $ret[] = $addr[$this->tokey['_ADDR']] ;
            }
        }
        if( 0 === count( $ret ) ){
            return $this->errorGather('No recipient' ,__LINE__) ;
        }else{
            $this->recipient = $ret ;
            return $this->errorGather();
        }
    }
    //------------------------------------------------------------------------
    // Attachment Routine
    //     attach - set to $this->attach array
    //        attach OneArray(1 array pattern array('path','attacheName'))
    //        attach Singe (2 string pattern  ('path','attacheName') )
    //           attachFull - Base Routine  allattch routine call him
    // buildAttach - called buildBody method
    //------------------------------------------------------------------------

    //
    //
    //array('path_filename','attach_name','mime_type','target_charset','org_charset', );
    //
    //
    //
    function attach( $param , $add = false ){
        list( $stack , $this->attach ) = array( $this->attach , array() );
        if(is_string($param)){
            $param = array($param);
        }

        if( ($this->inline_mode || $this->simple_attach) && !is_array($param[0])){
            foreach($param as $one){
                $param_temp[] = array( $one );
            }
            $param = $param_temp;
        }

        $te_st = reset($param);
        if(!is_array($te_st)){
            $param = array( $param );
        }

        foreach($param as $par){
            if(empty($par)){
                continue;
            }
            $path_filename = isset($par['PATH']) ? $par['PATH']:$par[0];

            if(isset($par['NAME'])){
                $attach_name = $par['NAME'];
            }elseif(isset($par[1])){
                $attach_name = $par[1];
            }else{
                $attach_name = basename( $path_filename ) ;
            }

            $mime_type = null;
            if(isset($par['CONTENT-TYPE'])){
                $mime_type = $par['CONTENT-TYPE'];
            }elseif(isset($par['MIME-TYPE'])){
                $mime_type = $par['MIME-TYPE'];
            }elseif(isset($par[2])){
                $mime_type = $par[2];
            }

            $content_id = null;
            if(isset($par['CONTENT-ID'])){
                $content_id = $par['CONTENT-ID'];
            }elseif(isset($par[3])){
                $content_id = $par[3];
            }

            $target_charset = null;
            if(isset($par['_CHARSET'])){
                $target_charset = $par['_CHARSET'];
            }elseif(isset($par[4])){
                $target_charset = $par[4];
            }
            $org_charset = null;
            if(isset($par['_ORG_CHARSET'])){
                $org_charset = $par['_ORG_CHARSET'];
            }elseif(isset($par[5])){
                $org_charset = $par[5];
            }
            $direct = null;
            if(isset($par['DIRECT'])){
                $direct = $par['DIRECT'];
            }elseif(isset($par[6])){
                $direct = $par[6];
            }
            $bare = false;
            if(isset($par['BARE'])){
                $bare = $par['BARE'];
            }elseif(isset($par[7])){
                $bare = $par[7];
            }

            $this->attach[] = array(
                'PATH'			=> $path_filename,
                'NAME'			=> $attach_name,
                'CONTENT-TYPE'	=> $mime_type,
                'CONTENT-ID'	=> $content_id,
                '_CHARSET'		=> $target_charset,
                '_ORG_CHARSET'	=> $org_charset,
                'DATA'			=> $direct,
                'DIRECT'		=> isset($direct) ,
                'BARE'			=> $bare,
            );
        }
        if($add){
            $this->attach = array_merge( $stack , $this->attach );
        }
        $this->attach_already_build = false ;
        return $this->errorGather() ;
    }

    //--------------------------------------------------------
    // Build attachment one file , called by buildBody method
    // $one is array , no recursive ,must ['PATH'] element
    //--------------------------------------------------------
    function buildAttach( $one , $boundary , $inline){
        $ret_boundary = null;
        $ret_header = array();
        $ret_content = null;
        $one = array_change_key_case( $one , CASE_UPPER);
        if( !isset($one['NAME'] )){
            $one['NAME'] = basename( $one['PATH'] );
        }
        //Content-Type
        if( isset( $one['CONTENT-TYPE'] )){
            $type = $one['CONTENT-TYPE'];
        }elseif( 0 != preg_match( '/\.([^\.]+)$/' , $one['NAME'] , $matches )){
            $type = isset( $this->attach_ctype[strtolower($matches[1])] )
                ? $this->attach_ctype[strtolower($matches[1])] : 'unkown';
        }elseif(0 != preg_match( '/\.([^\.]+)$/' , $one['PATH'] , $matches )){
            $type = isset( $this->attach_ctype[strtolower($matches[1])])
                ? $this->attach_ctype[strtolower($matches[1])] : 'unkown';

            if( $this->auto_ext && 'unkown' != $type ){
                $one['NAME'] .= '.'.$matches[1];
            }

        }else{
            $type = 'unkown';
        }

        if( isset( $one['_CHARSET'] ) ){
            $charset = $one['_CHARSET'];
        }else{
            $charset = $this->charset_attach_filename;
        }
        if( isset( $one['_ORG_CHARSET'] ) ){
            $org_charset = $one['_ORG_CHARSET'];
        }else{
            $org_charset = null;
        }

        $filename = $this->mimeEncode( $one['NAME'] , $charset , $org_charset , 20 );

        //is Inline ?
        if( $inline ){
            $id = $this->content_id_fix ? $one['CONTENT-ID']:$this->makeContentId($one['CONTENT-ID']);
            $content_id =  '<' . $id . '>' ;
            $content_disposition = 'inline';//attachment for au?
        }else{
            $content_id =  null ;
            $content_disposition = 'attachment';
        }

        // do it need Disposition Heaer ?
        if( isset( $this->deco_kind ) && false===$this->deco_def[$this->deco_kind]['DISPOSITION']){
            $disposition = null;
        }else{
            $disposition = $content_disposition.';'.$this->LFC
                .' filename="'.$filename.'"'
            ;
        }
        $ret_boundary = '--'.$boundary ;
        $ret_header['Content-Type'] = $type.'; name="'.$filename.'"'
        ;
        $ret_header['Content-Transfer-Encoding'] = 'base64' ;
        $ret_header['Content-Id'] = isset($content_id) ? trim( $content_id ) : null ;
        if(!empty($disposition)){
            $ret_header['Content-Disposition'] = $disposition;
        }
        if( !empty( $one['DIRECT'] ) ){
            $cont=$one['DATA'];
        }else{
            $path_filename = $this->attachPathFix( $one['PATH'] );
            if( !file_exists ( $path_filename )){
                $this->error[]='No attach file \''.$path_filename.'\' line->'.__LINE__;
                return false;
            }else{
                $cont=file_get_contents( $path_filename );
            }
        }
        if( isset( $one['BARE'] ) && true === $one['BARE'] ){
            $ret_content = $one['DATA'];
        }else{
            $ret_content = trim(chunk_split(base64_encode($cont)));
        }
        $this->attach_already_build = true;
        return array(
            'BOUNDARY' =>$ret_boundary ,
            'HEADER' =>$ret_header ,
            'CONTENT' =>$ret_content
        );

    }
    function isSetCid( $array ){
        return  isset( $array['CONTENT-ID'] ) && ( '' !== $array['CONTENT-ID']  ) ;
    }
    function makeContentId( $id ){
        if( is_null( $this->after_id ) ){
            $fromaddr = isset($this->header['From'][0]) ? $this->extractAddr($this->header['From'][0]):null;
            $this->after_id = mt_rand() . '_'. str_replace(array('@','-','/','.'),'', $fromaddr .'_'. $this->xmailer.'_'.$this->version );
        }
        return str_replace(array('@','-','/','.'),'', 'id_'.$id ).'_@_'. $this->after_id ;
    }
    function replaceCid( $content ){
        if( !isset( $this->deco_kind ) ){
            return $content;
        }
        foreach($this->attach as $att){
            if( $this->isSetCid( $att ) ){
                $orig = preg_quote($att['CONTENT-ID'] ,'/');
                $rep = $this->makeContentId(  $att['CONTENT-ID'] );
                $content = preg_replace('/(<\s*img[^>]+src\s*=\s*"cid:)' . $orig . '("[^>]*>)/is','${1}'.$rep.'${2}' ,$content);
            }
        }
        return $content ;
    }
    function attachDirect( $attach_name , $data , $add = false , $mime_type = null , $content_id = null , $target_charset = null , $charset_org = null){
        $_att=array();
        $_att[0]['DIRECT'] = true;
        $_att[0]['DATA'] = $data;
        $_att[0]['PATH'] = null;
        $_att[0]['NAME'] = $attach_name ;
        $_att[0]['MIME_TYPE'] = $mime_type ;
        $_att[0]['CONTENT-ID'] = $content_id ;
        $_att[0]['_CHARSET'] = $target_charset;
        $_att[0]['_ORG_CHARSET'] = $charset_org;
        if( $add ){
            $this->attach = array_merge( $this->attach , $_att );
        }else{
            $this->attach = $_att ;
        }
        return $this->errorGather() ;
    }
    function attachPathFix( $path_filename ){
        $temp = substr($path_filename,0,1);
        if( '/' != $temp && '\\' != $temp ){
            return  $this->attach_path . $path_filename;
        }
        return $path_filename;
    }

    //---------------------------------------
    //
    // Inteligent Multibyte Wordwrap
    //
    //---------------------------------------
    function mbWordwrap( $word , $length ){
        if( !is_numeric( $length ) ){
            $length = $this->wordwrap_length;
        }
        if( 1 > $length ){
            $this->error[]='Wordwrap length illegal , need more than 1 line->'.__LINE__;
        }
        $ret = array();
        list( $word , $LFC ) = $this->clean( $word );
        $lines = explode( $LFC , $word ) ;
        foreach($lines as $line){
            $ret []= $this->mbWordwrapLine( $line , $length );
        }
        return implode( $this->LFC , $ret );
    }
    function mbWordwrapLine( $line , $length ){
        $skip = false;
        if( 0 != count( $this->wrap_except ) ){
            foreach($this->wrap_except as $word => $begin_flag ){
                $fg = strpos( $line , $word );
                if( ( ( 0 === $fg ) && $begin_flag) || ( ( false !== $fg ) && !$begin_flag) ){
                    $skip = true;
                }
            }
        }
        $enc = $this->qd_detect_encoding( $line );
        $len = mb_strlen( $line , $enc );
        if ( ( $len <= $length )  || $skip ) {
            return $line;
        }

        if( $this->wrap_width ){
            $method = 'widthSubStr';
        }else{
            $method = 'defMbSubStr';
        }

        $ret = array();
        $ln = $length;
        $j = 0;
        for( $i=0; $i < $len ; $i += $ln ){
            list( $r , $ln ) = $this->{$method}( $line , $i , $length , $enc );
            if( 0 !== $j ){
                list( $r , $no_top , $flag )=$this->mbProhibitTop( $r , $enc );
                if( $flag ){
                    $ret[$j-1] .= $no_top ;
                    $i += mb_strlen( $no_top , $enc );
                    list( $r , $ln ) = $this->{$method}( $line , $i , $length , $enc );
                }
            }
            if( ( $i + $ln ) < $len  ){
                list( $_r , $ret_count , $flag )=$this->mbProhibitEnd( $r , $enc );
                if( $flag && ( $ret_count < ($length-1) ) ){
                    $i -=  $ret_count;
                    $r = $_r;
                }
            }
            $ret [$j++]= $r ;
        }
        return implode( $this->LFC , $ret ) ;
    }

    function defMbSubStr( $line , $start , $length , $enc ){
        return array( mb_substr( $line , $start , $length , $enc ) ,$length );
    }
    function widthSubStr( $line , $start , $length , $enc ){
        $ret = array();
        $max = mb_strlen( $line , $enc ) ;
        $target = mb_substr( $line , $start , $length , $enc ) ;
        $point = $start + $length;
        // mb_strwidth's lengh means ascii width
        while( ( mb_strwidth( $target , $enc ) <= ( $length-1 ) * $this->mb_strwidth_magni ) && ( $point < $max ) ){
            $target .= mb_substr( $line , $point++ , 1 , $enc ) ;
        }
        return array( $target , mb_strlen( $target , $enc ) ) ;
    }

    function mbProhibitTop(  $line , $enc ){
        $flag = false;
        $ret = null;
        $len = mb_strlen( $line , $enc );
        $count = 0 ;
        do{
            $top = mb_substr( $line , $count++ , 1 , $enc );
        }while( isset($this->wrap_prohibit[$top]) && $this->wrap_prohibit[$top] && ( abs($count) < $len  ) );
        -- $count ;
        if( 0 < $count ){
            $ret = mb_substr( $line , 0 , $count , $enc );
            $line = mb_substr( $line , $count , $len - $count , $enc );
            $flag = true;
        }
        return array( $line , $ret , $flag );
    }
    function mbProhibitEnd( $line , $enc ){
        $flag = false;
        $len = mb_strlen( $line , $enc );
        $count = 0 ;
        do{
            $end = mb_substr( $line , --$count , 1 , $enc );
        }while( isset($this->wrap_prohibit[$end]) && !$this->wrap_prohibit[$end] && ( abs($count) < $len  ) );
        $count = abs( ++$count );
        if( 0 < $count ){
            $line = mb_substr( $line , 0 , $len - $count , $enc );
            $flag = true;
        }
        return array( $line , $count , $flag );
    }
    //------------------------
    // utility
    //------------------------
    function issetContent( $array ){
        if( !isset( $array ) ){
            return false ;
        }
        if( isset( $array['CONTENT'] ) ){
            return true ;
        }
        if( isset( $array ) && is_string( $array ) ){
            return true ;
        }
        return false ;
    }
    function keyUpper( $array ){
        $up_array = array_change_key_case( $array , CASE_UPPER );
        $link = $this->qdmail_array_combine( array_keys( $up_array ) , array_keys( $array ));
        return array( $up_array , $link );
    }
    function qdmail_array_combine( $keys , $values ){//for php4
        if( !is_array( $keys ) || !is_array( $values ) ){
            $this->error[]='array_conbine needs array line =>'.__LINE__;
        }
        $ret = array();
        reset( $values );
        foreach( $keys as $key ){
            $ret[$key] = array_shift( $values ) ;
        }
        return $ret;
    }
    function qdmail_md( $col = null , $start = 33 , $end = 126 ){
        if( is_null( $col ) ){
            $col = $this->boundary_base_degit ;
        }
        $ret = null;
        for( $i = 0 ; $i < $col ; $i++){
            $ret .= chr( mt_rand( $start , $end ) ) ;
        }
        return $ret;
    }

    function clean( $content ){
        if($this->smtp ){
            $LFC = $this->LFC;
        }else{
            $LFC = chr(10);
        }
        return array(rtrim( preg_replace( '/\r?\n/' , $LFC , $content ) ),$LFC);
    }
    function quotedPrintableEncode( $word ){
        if(empty($word)){
            return $word;
        }
        $lines = preg_split("/\r?\n/", $word);
        $out = array() ;
        foreach ($lines as $line){
            $one_line = null ;
            for ($i = 0; $i <= strlen($line) - 1; $i++){
                $char = substr ( $line, $i, 1 );
                $ascii = ord ( $char );
                if ( (32 > $ascii) || (61 == $ascii) || (126 < $ascii) ){
                    $char = '=' . strtoupper ( dechex( $ascii ) );
                }
                if ( ( strlen ( $one_line ) + strlen ( $char ) ) >= 76 ){
                    $out[]= $one_line . '=' ;
                    $one_line = null ;
                }
                $one_line .= $char;
            }
            $out[]= $one_line  ;
        }
        return implode( $this->LFC , $out );
    }
    function time(){
        list($start_usec, $start_sec) = explode(' ', $this->start_time );
        list($end_usec, $end_sec) = explode(' ', microtime() );
        return ($end_sec - $start_sec) + (float) ($end_usec - $start_usec);
    }
    //------------------
    // error
    //------------------
    function errorStatment( $type = false , $lf = null ){
        if( $type ){
            return $this->errorRender( $this->error_stack , $lf , false );
        }else{
            return $this->error_stack;
        }
    }

    function errorRender( $error = null , $lf = null , $display = true ){
        if( is_null( $error ) ){
            $error = $this->error;
        }
        if( is_null( $lf ) ){
            $lf = $this->log_LFC ;
        }
        if( !is_array( $error ) ){
            $error = array( $error );
        }
        $out = null ;
        foreach($error as $mes){
            $out .= $this->name . ' error: ' . trim( $mes ) . $lf ;
        }
        if( $this->error_display && $display ){
            $_out = str_replace( $lf ,'<br>' . $lf , $out );
            echo  $_out ;
        }
        return $out ;
    }

    function errorGather( $message = null , $line = null){

        if( !is_null( $message ) ){
            if( !is_null( $line ) ){
                $message .= ' line -> '.$line;
            }
            if(0 === count( $this->error_stack )){
                $this->error[] = 'Qdmail Version '.$this->version.' ,PHP Version '.phpversion();
                $this->error[] = $this->iniGather();
            }
            $this->error[] = $message ;
        }elseif( 0 === count( $this->error )){
            return true;
        }

        $er = $this->errorRender();
        $this->error_stack = array_merge( $this->error_stack , $this->error );
        $this->error = array();

        if( !$this->logWrite( 'error' ,  $er ) ){
            $this->error_stack = array_merge( $this->error_stack , $this->error );
        }
        return false;
    }

    function log( $mes = null ){
        if( is_null( $mes )){
            $addrs = $this->done() ;
//			$this->done = array();
            $spacer = null;
            if( 0 != count( $addrs ) ){
                $mes .= 'Send Success: '.implode(' ',$addrs) ;
                $spacer =  $this->log_LFC ;
            }
            $addrs = $this->undone() ;
//			$this->undone = array();
            if( 0 != count( $addrs ) ){
                $mes .=  $spacer . 'Send failure: '.implode(' ',$addrs);
            }
        }
        return $this->logWrite( null , trim( $mes ) ) ;
    }

    function logWrite( $type , $message ){

        $tp = ('error' == $type) ? false:true;
        $level		=	$tp ? $this->log_level:$this->errorlog_level;
        $fg_debug = ( 2 > $this->debug ) && !$this->render_mode;
        if( 0 == $level || !$fg_debug){
            return true;
        }

        $filename	=	$tp ? $this->log_filename:$this->errorlog_filename;
        $path		=	$tp ? $this->log_path:$this->errorlog_path;
        $ap			=	$tp ? $this->log_append:$this->errorlog_append;
        $fp = fopen( $path.$filename , $ap );
        if( !is_resource( $fp ) ){
            $this->error[]='file open error at logWrite() line->'.__LINE__;
            return false;
        }
        $spacer = $tp ? ' ' : $this->log_LFC ;
        fwrite( $fp ,
            date( $this->log_dateformat )
            . $spacer
            . trim( $message )
            . $this->log_LFC
        );
        if( $level > 1 ){
            fwrite( $fp , trim( $this->header_for_smtp ) . $this->log_LFC );
        }elseif( $level > 2 ){
            fwrite( $fp , $this->log_LFC .  $this->content_for_mailfunction  . $this->log_LFC );
        }
        fclose( $fp ) ;
        return true ;
    }

    function errorSpecify( $func , $line , $add_message = null){
        return $this->errorGather($add_message.'User Specify Error in Method of \''.$func.'\'' , $line ) ;
    }
    //-------------------------------------------
    // MIME Content-type def
    //-------------------------------------------
    var $attach_ctype=array(
        'txt'=>'text/plain',
        'csv'=>'text/csv',
        'xml'=>'text/xml',
        'htm'=>'text/html',
        'html'=>'text/html',
        'gif'=>'image/gif',
        'jpg'=>'image/jpeg',
        'jpeg'=>'image/jpeg',
        'png'=>'image/png',
        'tif'=>'image/tiff',
        'tiff'=>'image/tiff',
        'bmp'=>'image/x-bmp',
        'ps'=>'appilcation/postscript',
        'eps'=>'appilcation/postscript',
        'epsf'=>'appilcation/postscript',
        'ai'=>'application/postscript',
        'zip'=>'application/zip',
        'lzh'=>'application/x-lzh',
        'lha'=>'application/octet-stream',
        'tar'=>'application/x-tar',
        'gzip'=>'application/x-tar',
        'cpt'=>'application/mac-compactpro',
        'doc'=>'application/msword',
        'xls'=>'application/vnd.ms-excel',
        'ppt'=>'application/vnd.ms-powerpoint',
        'rtf'=>'application/rtf',
        'pdf'=>'application/pdf',
        'css'=>'application/css',
        'au'=>'audio/basic',
        'rpm'=>'audio/x-pn-realaudio-plugin',
        'swa'=>'application/x-director',
        'mp3'=>'audio/mpeg',
        'mp4'=>'audio/mp4',
        'wav'=>'audio/x-wav',
        'midi'=>'audio/midi',
        'avi'=>'vide/x-msvideo',
        'mpeg'=>'video/mpeg',
        'mpg'=>'video/mpeg',
        'wmv'=>'video/x-ms-wmv',
        'flash'=>'application/x-shockwave-flash',
        'mmf'=>'application/x-smaf ',	//softbank chakumero
        'smaf'=>'application/x-smaf',	//softbank chakumero
        'hdml'=>'text/x-hdml',			// HDML au,docomo
        '3gpp2'=>'video/3gpp2',			// au chaku-uta,ez-movie
        '3g2'=>'video/3gpp2',			// au chaku-uta,ez-movie
        'amc'=>'video/3gpp2',			// au chaku-uta,ez-movie
        'kjx'=>'application/x-kjx',		// au ez-apri
        '3gpp'=>'video/3gpp',			// docomo chaku-uta,movie
        '3gp'=>'video/3gpp',			// docomo chaku-uta,movie
        'amr'=>'video/3gpp', 			// docomo chaku-uta,movie
        'asf'=>'video/3gpp',			// docomo chaku-uta,movie
        'jam'=>'application/x-jam',		// docomo i-apri
        'jar'=>'application/java-archive',	// java apri
        'jad'=>'text/vnd.sun.j2me.app-descriptor',	// java apri
        'exe'=>'application/octet-stream',
        'khm'=>'application/x-kddi-htmlmail',// au decoration mail template
        'dmt'=>'application/x-decomail-template',// nttdocomo decoration mail template
        'hmt'=>'application/x-htmlmail-template',// softbank decoration mail template
        'hqx'=>'application/mac-binhex40',
        'cpt'=>'application/mac-compactpro',
        'php'=>'application/x-httpd-php',
        'php4'=>'application/x-httpd-php',
        'php3'=>'application/x-httpd-php',
        'phtml'=>'application/x-httpd-php',
        'phps'=>'application/x-httpd-php-source',
        'js'=>'application/x-javascript',
        'swf'=>'application/x-shockwave-flash',
        'eml'=>'message/rfc822',
    );

    //-------------------------------
    // Debug
    //-------------------------------
    function iniGather(){
        $ret  = 'OS '.PHP_OS.' ; PHP Version '.PHP_VERSION.' ; '.$this->name.' version '.$this->version;
        $ret .= $this->LFC.'php.ini status: mb_language = '.mb_language()
            .' ; mb_internal_encoding = '.mb_internal_encoding()
            .' ; mb_detect_order = '.implode(',',mb_detect_order());
        $ret .= $this->LFC . $this->name .' Status debug: '.$this->debug().', log: '.$this->logLevel().', errorlog: '.$this->errorlogLevel();
        return $ret;
    }

    function debug( $level=null ){
        if( is_null( $level ) || !is_numeric($level) ){
            return $this->debug;
        }
        $this->debug = $level ;
        return true;
    }
    function debugEchoLine(){
        $vars = func_get_args();
        $this->debugEcho( false , $vars );
    }
    function debugEchoLf(){
        $vars = func_get_args();
        $this->debugEcho( true , $vars );
    }
    function debugEcho( $lf , $vars = null ){
        static $already_header = false;
        static $already_footer = false;
        if( 1 > $this->debug ){
            return;
        }

        if( true === $this->debug_echo_charset ){
            $this->debug_echo_charset = $this->charset_content ;
        }

        if( !$already_header ){
            $head='<html><head><meta http-equiv="content-type" content="text/html; charset='.$this->debug_echo_charset.'"></head><body>';
            echo $head ;
            echo '<pre>'.$this->name . ' Debug: '.$this->iniGather().'</pre>';
            $already_header = true ;
        }
        if( $already_header && ( 'END' === $lf ) && !$already_footer){
            $foot ='</body></html>';
            echo $foot;
            $already_footer = true;
            return ;
        }
        $out = null;
        if( !is_array( $vars ) ){
            $vars =array( $vars );
        }
        foreach($vars as $var){
            $_out = print_r( $var , true ) ;
            $enc = $this->qd_detect_encoding( $_out );
            if( ( strtoupper( $this->qdmail_system_charset ) !== strtoupper( $enc ) ) && ('ASCII'!==strtoupper( $enc ))){
                $_out = $this->qd_convert_encoding( $_out , $this->qdmail_system_charset , $enc );
            }
            $out .=  $_out  . $this->LFC;
        }
        $spacer = $this->log_LFC ;
        if( !$lf ){
            $out = preg_replace("/\\r?\\n/",' ',$out);
            $spacer = null ;
        }

        echo "<pre>";
        $out = $this->name . ' Debug: ' . $spacer . trim( $out );
        $out = htmlspecialchars( $out , ENT_QUOTES ,  $this->qdmail_system_charset);
        $out = $this->qd_convert_encoding($out,$this->debug_echo_charset,$this->qdmail_system_charset);
        echo $out;
        echo "</pre>";

    }
    function debugReport( $var = null ){
        if( is_null( $var ) ){
            return $this->debug_report;
        }
        if( is_bool( $var ) ){
            $this->debug_report = $var;
            return true;
        }
        if( 'FILE' !== $var ){
            return $this->errorSpecify(__FUNCTION__,__LINE__);
        }

        $fg= true;
        $cont = print_r( $this , true );
        $cont .= print_r( $_SERVER , true);
        $date = date("Y_m_d_H_i_s");
        $out = <<<EOF
Debug Report
date: {$date}
name: {$this->name}
version: {$this->version}

{$cont}
EOF;

        $filename = $this->debug_report_path . $this->name.'_debug_report_'.date("Y_m_d_H_i_s") . '.txt';
        if($fp = fopen( $filename , 'w' )){
            fwrite( $fp , $out );
            fclose( $fp );
        }else{
            $this->error[] = 'Can not open file \'' . $filename . '\' line-> ' . __LINE__ ;
            $fg = false;
        }
        return $fg;
    }
    //--
    // this path like this, /home/foo/bar/  or c\:htdocs\foo\bar\ or ./foo/bar/
    // do not forget the last '/' or '\'
    //--
    function debugReportPath( $path = null ){
        if( is_null( $path ) ){
            return $path;
        }
        if( empty( $path ) ){
            $this->debug_report_path = './';
            return true;
        }
        if( is_string( $path ) ){
            $this->debug_report_path = $path;
            return true ;
        }
        return $this->errorSpecify(__FUNCTION__,__LINE__);
    }

    function sendBySmtp( $obj = null ){
        if( !is_null( $obj ) ){
            $this->smtp_object = $obj;
        }
        if( !isset( $this->smtp_object ) || !is_object( $this->smtp_object ) ){
            if( false === ( $this->smtp_object = $this->smtpObject() ) ){
                return $this->errorGather('SMTP Object make \'new\' error',__LINE__);
            }
        }
        $this->smtp_server = array_change_key_case( $this->smtp_server , CASE_UPPER );

        if( !isset( $this->smtp_server['HOST'] ) ){
            $this->smtp_server = array_merge( $this->smtp_server , $this->smtp_object->server() );
        }
        if( !isset( $this->smtp_server['HOST'] ) ){
            return $this->errorGather('No exist SMTP\'s Settings',__LINE__);
        }
        $this->smtp_server['CONTINUE'] = true;

        if( !$this->smtp_object -> server( $this->smtp_server )){
            return $this->errorGather('SMTP Object initialize error',__LINE__);
        }
        if( $this->smtp_loglevel_link ){
            $this->smtp_object -> logLevel( $this->log_level );
            $this->smtp_object -> errorlogLevel( $this->errorlog_level );
        }
        $this->smtp_object -> to( $this->recipient );
        $this->smtp_object -> data( $this->content_all_for_smtp );
        return $this -> smtp_object -> send();
    }

    function sendBySendmail(){

        $temp_name = tempnam ( $this->temporary_path , 'qdmail' );
        if(false===$temp_name){
            return $this->errorGather('Can not make Temporary File ',__LINE__);
        }
        $fp = fopen($temp_name,'w');
        if(false===$temp_name){
            return $this->errorGather('Can not open Temporary File ',__LINE__);
        }
        fputs($fp,$this->content_all_for_smtp);
        fclose($fp);
        $recipient = implode(' ',$this->recipient);
        $sendfg = exec($this->sendmail_path . ' '.$this->mtaOption().' '.$recipient.' < '.$temp_name,$ret);
        $fg = unlink($temp_name);
        if(false===$fg){
            return $this->errorGather('Can not dellete Temporary File ',__LINE__);
        }
        return (false!==$sendfg && empty($sendfg)) || true === $sendfg;

    }
    //------------------------------------------
    // expecting Override on the other FrameWork
    //------------------------------------------
    function & smtpObject( $null = false ){
        if(is_null($null)){
            $this->smtp_object = null;
            return true;
        }
        if( isset( $this->smtp_object ) && is_object( $this->smtp_object ) ){
            return $this->smtp_object;
        }
        if( !class_exists ( 'Qdsmtp' ) && file_exists( 'qdsmtp.php' ) ){
            require_once( 'qdsmtp.php' );
        }elseif( !class_exists ( 'Qdsmtp' ) && !file_exists( 'qdsmtp.php' )){
            return $this->errorGather('Plese load SMTP Program - Qdsmtp http://hal456.net/qdsmtp',__LINE__);
        }
        $this->smtp_object = new Qdsmtp();
        return $this->smtp_object;
    }
    function setSmtpObject( & $obj ){
        if(is_object($obj)){
            $this->smtp_object = & $obj;
            return true;
        }else{
            return false;
        }
    }

}//the QdmailBase

class QdmailUserFunc extends QdmailBase{

    function __construct( $param = null ){
        parent::__construct( $param );
    }

    function validateAddr( $addr ){
        if(0==preg_match( $this->varidate_address_regex , $addr , $match )){
            $this->errorGather('Tyr Varidate Error by regex preg_match(\''.$this->varidate_address_regex . '\') the address is ->'.$addr,__LINE__);
        }else{
            return true;
        }
    }

    function stripCrlf( $word ){
        if( $this->force_change_charset ){
            $enc = $this->qd_detect_encoding( $word ) ;
            $word = $this->qd_convert_encoding( $word , $this->qdmail_system_charset , $enc );
        }
        $word = preg_replace( '/\r?\n/i' , '' , $word );
        if( $this->force_change_charset ){
            $word = $this->qd_convert_encoding( $word , $enc , $this->qdmail_system_charset );
        }
        return $word ;
    }

}

class Qdmail extends QdmailUserFunc{

    var $name ='Qdmail';

    function Qdmail( $param = null ){
        if( !is_null($param)){
            $param = func_get_args();
        }
        parent::__construct( $param );
    }
}

//-------------------------------------------
// CakePHP Component
//-------------------------------------------
class QdmailComponent extends QdmailUserFunc{

    var $framework	= 'CakePHP';
    var $view_dir	= 'email';
    var $layout_dir	= 'email';
    var $layout		= 'default';
    var $template	= 'default';
    var $view		= null;

    function QdmailComponent( $param = null ){
        if( !is_null($param)){
            $param = func_get_args();
        }
        parent::__construct( $param );
    }

    function startup(&$controller) {
        $this->Controller =& $controller;
        if( defined( 'COMPONENTS' ) ){
            $this->logPath(COMPONENTS);
            $this->errorlogPath(COMPONENTS);
        }
        return;
    }
    //----------------------------
    // Override Parent Method
    //----------------------------
    function & smtpObject(){
        if( isset( $this->Qdsmtp ) && is_object( $this->Qdsmtp ) ){
            return $this->Qdsmtp;
        }

        if( !class_exists ( 'QdsmtpComponent' ) ){
            if( !$this->import( 'Component' , 'Qdsmtp' ) ){
                return $this->errorGather('Qdmail<->CakePHP Component Load Error , the name is Qdsmtp',__LINE__);
            }
        }
        $this->Qdsmtp = new QdsmtpComponent();
        if( !is_object( $this->Qdsmtp ) ){
            return $this->errorGather('Qdmail<->CakePHP Component making Instance Error , the name is QdsmtpComponent',__LINE__);
        }
        $this->Qdsmtp -> startup( $this->Controller );
        return $this->Qdsmtp;
    }
    //----------------------------
    // Cake Interface
    //----------------------------
    function import( $kind , $name ){
        if( 1.2 > (float) substr(Configure::version(),0,3) ){
            $function_name = 'load' . $kind ;
            if( function_exists( $function_name ) ){
                return $function_name( $name ) ;
            }else{
                return $this->errorGather('Qdmail<->CakePHP ' .$kind .' Load Error , the name is \'' . $name . '\'',__LINE__);
            }
        }else{
            return App::import( $kind , $name ) ;
        }
    }
    function cakeText( $content , $template = null , $layout = null , $org_charset = null , $target_charset = null , $enc = null , $wordwrap_length = null ){

        $this->template = is_null( $template ) ?  $this->template : $template ;
        $this->layout   = is_null( $layout )   ?  $this->layout : $layout ;

        list( $cont , $target_charset , $org_charset ) = $this->cakeRender( $content , 'TEXT' , $org_charset = null , $target_charset );
        return $this->text(  $cont , $wordwrap_length , $target_charset , $enc , $org_charset );
    }
    function cakeHtml( $content , $template = null , $layout = null , $org_charset = null , $target_charset = null , $enc = null ){

        $this->template = is_null( $template ) ?  $this->template : $template ;
        $this->layout   = is_null( $layout )   ?  $this->layout : $layout ;

        list( $cont , $target_charset , $org_charset ) = $this->cakeRender( $content , 'HTML' , $org_charset = null , $target_charset );
        return $this->html(  $cont , null , $target_charset , $enc , $org_charset  );
    }
    function cakeRender( $content , $type , $org_charset = null , $target_charset = null){

        if( is_null( $target_charset ) ){
            $target_charset = $this->charset_content;
        }
        if( !class_exists ( $this->Controller->view ) ){
            if( !$this->import( 'View' , $this->view ) ){
                return $this->errorGather('Qdmail<->CakePHP View Load Error , the name is \''.$this->view.'\'',__LINE__);
            }
        }
        $type = strtolower( $type );
        $view = new $this->Controller->view( $this->Controller , false );
        $view->layout = $this->layout;
        $mess = null;
        $content = $view->renderElement( $this->view_dir . DS . $type . DS . $this->template , array('content' => $content ) , true );
        if( 1.2 > (float) substr(Configure::version(),0,3) ){
            $view->subDir = $this->layout_dir . DS . $type . DS ;
        }else{
            $view->layoutPath = $this->layout_dir . DS . $type;
        }
        $mess .= $view->renderLayout( $content ) ;

        if( is_null( $org_charset ) ){
            $org_charset = $this->qd_detect_encoding( $mess );
        }
        $mess = $this->qd_convert_encoding( $mess , $target_charset , $org_charset );
        return array( $mess , $target_charset , $org_charset );
    }
    function CakePHP( $param ){
        $param = array_change_key_case( $param , CASE_LOWER );
        extract($param);
        if( isset($type) || 'HTML' == $type ){
            $type ='cakeHtml';
        }else{
            $type = 'cakeText';
        }
        return $this->{$type}( isset($content) ?  $content:null, isset($template) ?  $template:null , isset($layout) ?  $layout:null , isset($org_charset) ?  $org_charset: null , isset($target_charset) ? $target_charset:null , isset($enc) ?  $enc:null , isset($wordwrap_length) ? $wordwrap_length:null );
    }
}
//-------------------------------------------
// Symfony Addon
//-------------------------------------------
class sfQdmail extends QdmailUserFunc{

    var $framework = 'Symfony';

    function __construct( $param = null ){
        if( !is_null($param)){
            $param = func_get_args();
        }
        parent::__construct( $param );
    }

    function setBody( $body ){
        if('HTML'===$this->is_html){
            $this->html( $body );
        }else{
            $this->text( $body );
        }
    }
    function getAltBody(){

        if('HTML'===$this->is_html){
            $content=$this->body();
            return !empty($content['TEXT']['CONTENT']);
        }else{
            return false;
        }
    }
    function setAltBody( $body ){
        $this->text( $body );
    }
    function addStringAttachment($data, $attach_name , $mime_type){
        $this->attachDirect( $attach_name , $data , $add = true , $mime_type );
    }
    function getRawHeader(){
        return $this->header_for_smtp;
    }
    function getRawBody(){
        return $this->$this->content_for_mailfunction;
    }

    function initialize(){
        $this->reset();
    }
    function setCharset($charset){
        $this->charset($charset);
    }
    function getCharset(){
        $ret = $this->charset();
        return $ret['TEXT'];
    }
    function setContentType($content_type){
        if(false===strpos(strtoupper($content_type),'HTML')){
            $this->is_html = 'TEXT';
        }else{
            $this->is_html = 'HTML';
        }
    }
    function getContentType(){
        if('HTML'===$this->is_html){
            return 'text/html';
        }else{
            return 'text/plain';
        }
    }
    function setPriority($priority){
        $pri = array(1=>'high',3=>'normal',5=>'low');
        if(isset($pri[$priority])){
            $this->priority($pri[$priority]);
            return true;
        }else{
            return false;
        }
    }
    function getPriority(){
        $pri = array('HIGH'=>1,'NORMAL'=>3,'LOW'=>5);
        $now_priority = strtoupper($this->priority());
        if(empty($now_priority)){
            return null;
        }
        return $pri[$now_priority];
    }
    function setEncoding($encoding){
        $this->encoding($encoding);
    }
    function getEncoding(){
        return $this->encoding();
    }
    function setSubject($subject){
        $this->subject($subject);
    }
    function getSubject(){
        return $this->subject();
    }
    function getBody(){
        $content=$this->body();
        if('HTML'===$this->is_html){
            return $content['HTML']['CONTENT'];
        }else{
            return $content['TEXT']['CONTENT'];
        }
    }
    function setMailer($type = 'mail', $options = array()){
        switch ($type){
            case 'smtp':
                $this->smtp = true;
                $this->sendmail = false;
                $this->mailer = 'smtp';
                if (isset($options['keep_alive'])){
                    $this->keepParameter(true);
                }
                break;
            case 'sendmail':
                $this->sendmail = true;
                $this->smtp = false;
                $this->mailer = 'sendmail';
                break;
            default:
                $this->smtp = false;
                $this->sendmail = false;
                $this->mailer = 'mail';
                break;
        }
    }
    function getMailer(){
        return $this->mailer;
    }
    function setSender($address, $name = null){
        $this->addHeader( 'Return-Path' , $address );
    }
    function getSender(){
        return isset($this->other_header['Return-Path']) ? $this->other_header['Return-Path']:null;
    }
    function setFrom($address, $name = null){
        $this->from( $address , $name );
    }
    function addAddresses($addresses){
        $this->to( $addresses , null , true );
    }
    function addAddress($address, $name = null){
        $this->to( $address , $name , true );
    }
    function addCc($address, $name = null){
        $this->cc( $address , $name , true );
    }
    function addBcc($address, $name = null){
        $this->cc( $address , $name , true );
    }
    function addReplyTo($address, $name = null){
        $this->replyto( $address , $name , true );
    }
    function clearAddresses(){
        $this->to =array();
    }
    function clearCcs(){
        $this->cc =array();
    }
    function clearBccs(){
        $this->bcc =array();
    }
    function clearReplyTos(){
        $this->replyto =array();
    }
    function clearAllRecipients(){
        $this->clearAddresses();
        $this->clearCcs();
        $this->clearBccs();
        $this->clearReplyTos();
    }
    function addAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream'){
        return $this->attach(array('PATH'=>$path,'NAME'=>$name,'MIME-TYPE'=>$type),true);
    }
    function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream'){
        return $this->attach(array('PATH'=>$path,'NAME'=>$name,'MIME-TYPE'=>$type,'CONTENT-ID'=>$cid),true);
    }
    function setAttachments($attachments){}
    function clearAttachments(){
        $this->attach=array();
    }
    function addCustomHeader($name, $value){
        $this->addHeader($name, $value);
    }
    function clearCustomHeaders(){
        $this->other_header=array();
    }
    function prepare(){}
    function smtpClose(){}
    function setDomain($hostname){}
    function getDomain(){}
    function setHostname($hostname){}
    function getHostname(){}
    function setPort($port){}
    function getPort(){}
    function setUsername($username){}
    function getUsername(){}
    function setPassword($password){}
    function getPassword(){}
    function setWordWrap($wordWrap){}
    function getWordWrap(){}
}

class QdDeco{

    var $template = null;
    var $data = array();

    function template( $template = null ){
        if(is_null($template)){
            return $this->template;
        }
        $this->template = trim(preg_replace("/\\r?\\n/is","\r\n",$template));
        return true;
    }

    function get( $kind ){
        if(!empty($this->data[$kind])){
            return $this->data[$kind];
        }
        if('ATTACH'===$kind){
            return array();
        }else{
            return null;
        }
    }

    function decode(){

        if(!class_exists('QdmailReceiver')){
            include('qd_mail_receiver.php');
        }

        $num_boundary = strpos(strtolower($this->template),'boundary');
        $num_crlf     = strpos($this->template,"\r\n\r\n");
        $template = $this->template;

        while((false!==$num_boundary)&&(false!==$num_crlf)&&($num_boundary > $num_crlf)){
            $template = substr($template,$num_crlf+4);
            $num_crlf = strpos($template,"\r\n\r\n");
            $num_boundary = strpos(strtolower($template),'boundary');
        }

        $receiver = QdmailReceiver::start( 'direct' , $template );
        $this->data['HTML'] = $receiver->bodyAutoSelect() ;
        if(false===$this->data['HTML']){
            $this->data['HTML'] = '';
        }
        $attach = $receiver->attach();
        foreach($attach as $att){
            if(isset($att['content-id'])){
                $cid = rtrim($att['content-id'],'>');
                $cid = ltrim($cid,'<');
            }elseif(isset($att['content_id'])){
                $cid = rtrim($att['content_id'],'>');
                $cid = ltrim($cid,'<');
            }else{
                $cid = null;
            }
            $this->data['ATTACH'][]=array(
                'PATH'			=> $att['filename_safe'],
                'NAME'			=> $att['filename_safe'],
                'CONTENT-TYPE'	=> $att['mimetype'],
                'CONTENT-ID'	=> $cid,
                '_CHARSET'		=> null,
                '_ORG_CHARSET'	=> null,
                'DIRECT'		=> $att['value'],
                'BARE'			=> false,
            );
        }
        return true;
    }
}?>