<?php
/*
 * This file is part of INQMAN
 *
 * Copyright(c) 2008 BULLHEAD,INC. ALL RIGHTS RESERVED.
 *
 * http://www.bullhead.co.jp/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation;
 * either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

require_once 'Zend/Mail/Storage/Pop3.php';

class Inqman_PopServer
{
    
    const ADDRESS_TYPE_NORMAL  = '1';
    const ADDRESS_TYPE_COMMENT = '2';
    
    /**
     * メール受信サーバの接続設定
     * @var array
     */
    protected $_settings;
    
    protected $_mail;
    
    /**
     * サービスID
     * @var integer
     */
    protected $_service_id;
    
    /**
     * サービスIDからメール受信サーバ設定を取得する
     * 
     * @access public
     */
    public function __construct($service_id)
    {
        $serviceModel = new Inqman_ServiceModel();
        $settings = (array) $serviceModel->getServiceSettings($service_id);

        foreach ($settings as $setting) {
            $_setting = array(
                    'type'     => $setting['type'],
                    'host'     => $setting['pop3_host'],
                    'port'     => $setting['pop3_port'],
                    'user'     => $setting['pop3_user'],
                    'password' => $setting['pop3_pass'],
            );
            $this->_settings[] = $_setting;
        }
        
        $this->_service_id = $service_id;
    }
    
    /**
     * メール受信による自動登録を実行する
     * 
     */
    public function perform()
    {
        
        $inquiry_count = 0;
        $reply_count   = 0;
        $comment_count = 0;
        foreach ((array)$this->_settings as $setting) {
            
            //受信済みのメール情報（ユニークID）を取得
            $maildump_model = new Inqman_MaildumpModel();
            $ids = $maildump_model->getReceivedUniqueId();
            
            try {
                //POP3サーバに接続
                $this->_connect($setting);
            } catch (Exception $e) {
                throw $e;
            }

            foreach ($this->_mail->getUniqueId() as $uid) {
                //サーバから受信したメールが受信済みでなければ登録処理を行う
                if(!in_array($uid, $ids)) {                
                    $number  = $this->_mail->getNumberByUniqueId($uid);
                    $message = $this->_mail->getMessage($number);
                    $values  = $this->_parse($message);
                    
                    //DBへ登録する
                    $values['type'] = $setting['type'];
                    $result = $this->_delivery($values);
                                        
                    if ($result == 'inquiry') $inquiry_count++;
                    elseif($result == 'reply') $reply_count++;
                    elseif($result == 'comment') $comment_count++;
                    
                    //メールをそのままDBに保存する
                    if (!empty($result)) {
                        $this->_saveMessage($uid, $message);
                    }
                }
            }
        }
        $result = array('inquiry_count' => $inquiry_count, 'reply_count' => $reply_count, 'comment_count' => $comment_count);
        return $result;
    }
    
    public function _delivery($values, $service_id=null)
    {        
        $inquiry_model = new Inqman_InquiryModel();
        $reply_model = new Inqman_ReplyModel();
        $mail_sender = new Inqman_MailSender();
        $user_model  = new Inqman_UserModel();
        
        if ($service_id == null) $service_id = $this->_service_id;
        
        $service_model = new Inqman_ServiceModel();
        $service = $service_model->getOneService($service_id);
        
        $service_code = $service['code'];
        $subject = $values['subject'];
        $inquiry_number = $this->_extractId($subject, $service_code);
        
        if (empty($inquiry_number)) {
            if ($this->_registerInquiry($values, $service_id)) {
                return 'inquiry';
            }
        } else {
            if (intval($values['type'])==1) {
                if ($this->_registerReply($values, $service_id, $inquiry_number)) {
                    return 'reply';
                }
            } else {
                if ($this->_registerComment($values, $service_id, $inquiry_number)) {
                    return 'comment';
                }
            }
        }
        return null;
    }
    
    public function _checkAclByMailaddress($mailaddress, $resource_key, $privilege_key, $service_id)
    {
        $user_model = new Inqman_UserModel();
        $acl_model = new Inqman_AclModel();
        
        $respondent = $user_model->getOneUserByMailaddress($mailaddress);
        $authorities = array_keys($acl_model->getUserRoles($respondent['id']));
        
        if (!$acl_model->checkAcl($resource_key, $privilege_key, $service_id, $authorities)) {
            throw new Exception(/*$this->_translate->translate('error.message.access_forbidden')*/);
        }
    }
    
    protected function _registerInquiry($values, $service_id)
    {
        $inquiry_model = new Inqman_InquiryModel();
        
        $inquiry = array(
            'subject'         => $values['subject'],
            'received_from'   => $values['from'],
            'priority'        => $values['priority'],
            'content'         => $values['content'],
            'service_id'      => $service_id,
            'status'          => Inqman_InquiryModel::STATUS_CODE_UNACCEPT,
            'create_datetime' => date("Y-m-d H:i:s", time()),
            'message_id'      => $values['message_id'],
        );
        $inquiry_id = $inquiry_model->createInquiry($inquiry);
        
        //メール送信
        $event_action_id = 2;
        $criteria = array('service_id'=>$service_id, 'inquiry_id'=>$inquiry_id);
        $inquiry_model->sendMail($event_action_id, $criteria);
        
        return true;
    }
    
    protected function _registerReply($values, $service_id, $inquiry_number)
    {
        $inquiry_model = new Inqman_InquiryModel();
        $user_model    = new Inqman_UserModel();
        $reply_model   = new Inqman_ReplyModel();
        
        $inquiry = $inquiry_model->getOneInquiryByNumber($service_id, $inquiry_number);
        $inquiry_id = $inquiry['id'];
        
        $status = intval($inquiry['status']);
        if ($status != intval(Inqman_InquiryModel::STATUS_CODE_UNASSIGN) && $status != intval(Inqman_InquiryModel::STATUS_CODE_PROCESS)) {
            //TODO ERROR
            return false;
        }
        
        preg_match('/([0-9a-zA-Z\-\.\_]+\@[0-9a-zA-Z\-\.]+)/', $values['from'], $matches);
        $mailaddress = $matches[0];
        
        $this->_checkAclByMailaddress($mailaddress, ACL_RESOURCE_KEY_REPLY, ACL_PRIVILEGE_KEY_CREATE, $service_id);
        $respondent = $user_model->getOneUserByMailaddress($mailaddress);
        
        $reply = array(
            'type'            => 1,
            'subject'         => $values['subject'],
            'received_from'   => $values['from'],
            'respondent_id'   => $respondent['id'],
            'priority'        => $values['priority'],
            'content'         => $values['content'],
            'inquiry_id'      => $inquiry_id,
            'level'           => 1,
            'reply_datetime'  => date("Y-m-d H:i:s", time()),
            'message_id'      => $values['message_id'],
        );
        
        if ($status == Inqman_InquiryModel::STATUS_CODE_UNASSIGN) {
            $inquiry_model->assignOperator($inquiry_id, $respondent['id']);
        } else if($inquiry['operator_id'] != $respondent['id']) {
            //TODO ERROR
            return false;
        }
        
        $reply_id = $reply_model->createReply($reply);
        
        //メール送信処理
        $criteria = array('service_id'=>$service_id, 'inquiry_id' => $inquiry_id, 'reply_id'=>$reply_id);
        $event_action_id = 12;
        $reply_model->sendMail($event_action_id, $criteria);
        if ($values['from'] != $inquiry['received_from']) {
            //問い合わせ送信者への返信
            $event_action_id = 11;
            $reply_model->sendMail($event_action_id, $criteria);
        }
        
        return true;
    }
    
    protected function _registerComment($values, $service_id, $inquiry_number)
    {
        $inquiry_model = new Inqman_InquiryModel();
        $user_model    = new Inqman_UserModel();
        $reply_model   = new Inqman_ReplyModel();
        
        $inquiry = $inquiry_model->getOneInquiryByNumber($service_id, $inquiry_number);
        $inquiry_id = $inquiry['id'];
        
        $status = intval($inquiry['status']);
        if ($status != intval(Inqman_InquiryModel::STATUS_CODE_UNASSIGN) && $status != intval(Inqman_InquiryModel::STATUS_CODE_PROCESS)) {
            //TODO ERROR
            return false;
        }
        
        preg_match('/([0-9a-zA-Z\-\.\_]+\@[0-9a-zA-Z\-\.]+)/', $values['from'], $matches);
        $mailaddress = $matches[0];
        
        $this->_checkAclByMailaddress($mailaddress, ACL_RESOURCE_KEY_COMMENT, ACL_PRIVILEGE_KEY_CREATE, $service_id);
        $respondent = $user_model->getOneUserByMailaddress($mailaddress);
        
        $reply = array(
            'type'            => 2,
            'subject'         => $values['subject'],
            'received_from'   => $values['from'],
            'respondent_id'   => $respondent['id'],
            'priority'        => $values['priority'],
            'content'         => $values['content'],
            'inquiry_id'      => $inquiry_id,
            'level'           => 1,
            'reply_datetime'  => date("Y-m-d H:i:s", time()),
            'message_id'      => $values['message_id'],
        );
        $reply_id = $reply_model->createReply($reply);
        
        //メール送信処理
        $criteria = array('service_id'=>$service_id, 'inquiry_id' => $inquiry_id, 'reply_id'=>$reply_id);
        $event_action_id = 10;
        $reply_model->sendMail($event_action_id, $criteria);
        
        return true;
    }
    
    
    protected function _extractId($subject, $service_code)
    {
        $inquiry_id = 0;
        if(!empty($subject)) {
            preg_match('/\['. $service_code .':[0-9]+\]/i', $subject, $match_str);
            if (isset($match_str[0])) preg_match('/[0-9]+/', $match_str[0], $match_number);
            if (isset($match_number[0])) $inquiry_id = intval($match_number[0]);
        }
        return $inquiry_id;
    }
    
    
    /**
     * 引数の設定情報に基づき、POP3サーバに接続します。
     * 
     * @access protected
     * @param array $config サーバ接続設定
     * 
     */
    protected function _connect($setting)
    {
        $this->_mail = new Zend_Mail_Storage_Pop3($setting);
    }
    
    /**
     * メールデータを解析し、件名、内容などの要素を配列に格納して返します。
     * 
     * @access protected
     * @param Zend_Mail_Message メールデータオブジェクト
     * @return mixed メールデータを解析して得た要素の配列
     * 
     */
    protected function _parse($message)
    {
        //$from    = mb_decode_mimeheader($message->from);
        //$subject = mb_decode_mimeheader($message->subject);
        //$subject = mb_convert_encoding($subject, "UTF-8", "ASCII,JIS,UTF-8,EUC-JP,SJIS");
        $from       = $message->from;
        $subject    = $message->subject;
        $priority   = $this->_getPriority($message->getHeaders());
        $message_id = $this->_getMessageId($message->getHeaders());
        //echo "Mail from '{$from}' : {$subject} : Priority:{$priority} \n";
    
        $content = "";
        if ($message->isMultipart()) {
            $partCount = $message->countParts();
            //echo "this mail has {$partCount} parts\n";
    
            for($i=1; $i<=$partCount; $i++) {
                $part = $message->getPart($i);
                if (strtok($part->contentType, ';') == 'text/plain') {
                    $content = mb_convert_encoding($part->getContent(), "UTF-8", "ASCII,JIS,UTF-8,EUC-JP,SJIS");
                }
            }
        } else {
            //echo "this mail has single parts\n";
            $content = mb_convert_encoding($message->getContent(), "UTF-8", "ASCII,JIS,UTF-8,EUC-JP,SJIS");
        }
        //echo "content:\n {$content}\n";
        
        $result = array(
                'from'       => $from,
                'subject'    => $subject,
                'priority'   => $priority,
                'content'    => $content,
                'message_id' => $message_id,
        );
        return $result;
    }
    
    /**
     * メール件名を解析し、返信先の問い合わせIDを取得します。
     * 
     * @access protected
     * @param string $subject メールの件名
     * @return integer $inquiryId 解析して得た問い合わせIDを返す。（無かった場合は0を返す）
     * 
     */
    protected function _parseSubject($subject)
    {
        preg_match ('/\[' . $prefix . '[0-9]+\]/',$subject,$match_string);
        
    }
    
    /**
     * メールデータを加工せずにDBに保存します。
     * 
     * @access protected
     * @param string $uniqueId メールを識別するユニークID
     * @param Zend_Mail_Message メールデータオブジェクト
     * 
     */
    protected function _saveMessage($uniqueId, $message)
    {
        //メールヘッダを取得し、ひとつの文字列にまとめる
        $header = "";
        foreach ($message->getHeaders() as $name => $value) {
            if (is_string($value)) {
                $header .= "{$name}: {$value}\n";
                continue;
            }
            foreach ($value as $entry) {
                $header .= "{$name}: {$entry}\n";
            }
        }
        //メールボディを取得する
        //TODO Content = Body? or Header+Body?
        //$content = $message->getContent();
        $parsed_message = $this->_parse($message);
        $content = $parsed_message['content'];
        
        $values = array(
                Inqman_MaildumpModel::UNIQUE_ID => $uniqueId,
                Inqman_MaildumpModel::HEADER    => $header,
                Inqman_MaildumpModel::CONTENT   => $content,
        );
        
        //DBに保存する
        $maildump_model = new Inqman_MaildumpModel();
        $maildump_model->create($values);
    }
        
    
    /*** check priority ***/    
    const HEADER_PRIORITY          = 'priority';
    const HEADER_X_PRIORITY        = 'x-priority';
    const HEADER_IMPORTANCE        = 'importance';
    const HEADER_X_MSMAIL_PRIORITY = 'x-msmail-priority';
    const HEADER_X_JSMAIL_PRIORITY = 'x-jsmail-priority';
    
    protected $_pCodeNumber = array(
                '1'      => Inqman_InquiryModel::PRIORITY_CODE_HIGH,
                '2'      => Inqman_InquiryModel::PRIORITY_CODE_XHIGH,
                '3'      => Inqman_InquiryModel::PRIORITY_CODE_MIDDLE,
                '4'      => Inqman_InquiryModel::PRIORITY_CODE_XLOW,
                '5'      => Inqman_InquiryModel::PRIORITY_CODE_LOW,
    );
    
    protected $_pCodeString = array(
                'high'   => Inqman_InquiryModel::PRIORITY_CODE_HIGH,
                'normal' => Inqman_InquiryModel::PRIORITY_CODE_MIDDLE,
                'low'    => Inqman_InquiryModel::PRIORITY_CODE_LOW,
    );
    
    protected $_defaultPriority = '30';
    
    /**
     * 
     * 
     */
    protected function _getPriority($headers)
    {
        $priorityHeader = array(
            self::HEADER_PRIORITY          => $this->_pCodeNumber,
            self::HEADER_IMPORTANCE        => $this->_pCodeString,
            self::HEADER_X_PRIORITY        => $this->_pCodeNumber,
            self::HEADER_X_MSMAIL_PRIORITY => $this->_pCodeString,
            self::HEADER_X_JSMAIL_PRIORITY => $this->_pCodeNumber,
        );
    
        $priority = $this->_defaultPriority;
        foreach ($headers as $name => $value) {
            if (strcasecmp($name, self::HEADER_X_PRIORITY) == 0) {
                $priority = $priorityHeader[self::HEADER_X_PRIORITY][$value];
                break;
            } else
            if (strcasecmp($name, self::HEADER_IMPORTANCE) == 0) {
                $priority = $priorityHeader[self::HEADER_IMPORTANCE][$value];
                break;
            } else
            if (strcasecmp($name, self::HEADER_X_MSMAIL_PRIORITY) == 0) {
                $priority = $priorityHeader[self::HEADER_X_MSMAIL_PRIORITY][$value];
                break;
            } else
            if (strcasecmp($name, self::HEADER_X_JSMAIL_PRIORITY) == 0) {
                $priority = $priorityHeader[self::HEADER_X_JSMAIL_PRIORITY][$value];
                break;
            } else
            if (strcasecmp($name, self::HEADER_PRIORITY) == 0) {
                $priority = $priorityHeader[self::HEADER_PRIORITY][$value];
                break;
            }
        }
        return $priority;
    }
    
    protected function _getMessageId($headers)
    {
        $message_id = "";
        foreach ($headers as $name => $value) {
            
            if (strcasecmp($name, 'Message-Id') == 0) {
                $message_id = $value;
            }
        }
        return $message_id;
    }
    
}

?>
