четверг, 24 июля 2014 г.

приключения с IMAP на php

В рамках корпоративной ERP-системы (упоминал в своей прошлой записи) разрабатывается web-mail-клиент (о как). Долго думал, использовать Pop3 или же imap. Все-таки выбрал imap, посмотрел в интернетах на наличие готовых решений. Нашел, но немного не того, пришлось пилить. Процесс, конечно еще продолжается, но костяк более-менее сделал (Скажу сразу, что дополнял класс. Он работает, но ряд вещей стоит все же переписать:

/*
 * Класс предназначен для считывания данных(писем) почты посредством работы (IMAP подключени к серверу)
 */
class ImapCheck
{
    private $mbox;
    private $htmlmsg;
    private $plainmsg;
    private $charset;
    private $attachments = array();
    private $unread;
    private $hcharset;
    private $filescount =0;

   /* public function __get($name)
    {
        if ($name=='mail') return $this->unread;
        else return null;
    }*/
    public function getmail()
    {
        return $this->unread;
    }

    /**
     * Конструктор
     * @param string $host  хост
     * @param string $login  аккаунт ящика
     * @param string $pwd  пароль
     * @param array $options
     * [letterID] = null|int - если известен ид письма
     * [search] =
     * ALL - return all messages matching the rest of the criteria
    ANSWERED - match messages with the \\ANSWERED flag set
    BCC "string" - match messages with "string" in the Bcc: field
    BEFORE "date" - match messages with Date: before "date"
    BODY "string" - match messages with "string" in the body of the message
    CC "string" - match messages with "string" in the Cc: field
    DELETED - match deleted messages
    FLAGGED - match messages with the \\FLAGGED (sometimes referred to as Important or Urgent) flag set
    FROM "string" - match messages with "string" in the From: field
    KEYWORD "string" - match messages with "string" as a keyword
    NEW - match new messages
    OLD - match old messages
    ON "date" - match messages with Date: matching "date"
    RECENT - match messages with the \\RECENT flag set
    SEEN - match messages that have been read (the \\SEEN flag is set)
    SINCE "date" - match messages with Date: after "date"
    SUBJECT "string" - match messages with "string" in the Subject:
    TEXT "string" - match messages with text "string"
    TO "string" - match messages with "string" in the To:
    UNANSWERED - match messages that have not been answered
    UNDELETED - match messages that are not deleted
    UNFLAGGED - match messages that are not flagged
    UNKEYWORD "string" - match messages that do not have the keyword "string"
    UNSEEN - match messages which have not been read yet
     * флаги для поиска
     */

    public function ImapCheck($host, $login, $pwd, $options = array('search'=>'ALL'))
    {
        $messages=array();
        $folder="INBOX";
        //$folder="[Gmail]/&BCEEPwQwBDw-"; // если вам захочется почитать спам на гугломыле.
        $this->mbox = imap_open ("{$host}$folder", $login,$pwd) or die(imap_last_error());//OP_HALFOPEN

        if(isset($options["search"]))
        {
            if(!isset($options["letterID"]) || $options["letterID"] == NULL)
            {
                $arr = imap_search  ($this->mbox, $options["search"]);

                if ($arr !== false)
                {
                    foreach ($arr as $i)
                        $messages[]=$this->getLEtter($i);

                    $this->unread=$messages;
                    unset($messages);
                }
                else {$this->unread=false;}
            }
            else
                $this->unread[$options["letterID"]]=$this->getLEtter($options["letterID"]);
        }
        else
            die("parametr options->search are empty!");
    }

    /**
     * заголовок от письма
     * @param $num номер письма в ящике
     * @return array
     */

    public function getHeaderInfo($num)
    {
        $headerArr = imap_headerinfo ( $this->mbox, $num);

        $elements = imap_mime_header_decode($headerArr->subject);
        $subj = "";

        for ($p = 0; $p < count($elements); $p++)
        {
            if($elements[$p]->charset !="default" && strtolower($elements[$p]->charset) !="utf-8")
            {
                $subj .= iconv($elements[$p]->charset, 'UTF-8', $elements[$p]->text);
                $this->charset = $elements[$p]->charset;
                $this->hcharset = $elements[$p]->charset;
            }
            else if($elements[$p]->charset =="default")
            {
                $subj.= iconv('KOI8-R','UTF-8', $elements[$p]->text);
                $this->charset = 'KOI8-R';
                $this->hcharset = 'KOI8-R';
            }
            else
            {
                $subj.=$elements[$p]->text;
                $this->charset = 'UTF-8';
                $this->hcharset = 'UTF-8';
            }
        }

        if(!isset($headerArr->sender[0]->personal))
            $headerArr->sender[0]->personal = "unknown";

        return array(
            'from'=> $headerArr->sender[0]->mailbox . "@" . $headerArr->sender[0]->host,
            'to'=> $headerArr->to[0]->mailbox . "@" . $headerArr->to[0]->host,
            'name'=> $this->decode($headerArr->sender[0]->personal) ,
            'subject'=>$subj,
            'Date'=>strtotime($headerArr->Date)
        );
    }

    /**
     * чтение письма по номеру
     * @param $num номер
     * @return array
     */
    public function getLetter($num)
    {

        $return = $this->getHeaderInfo($num);
        $this->getmsg($num); //читаем тело письма (сообщение)

        //imap_setflag_full($this->mbox, $i, "\\Seen"); //прочли письмо, поставили флаг, что прочли

        $return["charset"] = $this->charset;
        $return["plain"] = $this->plainmsg;
        $return["html"] = $this->htmlmsg;
        $return["attach"] = $this->attachments;
        $return["letterID"] = $num;

        return $return;
    }

    private function decode($enc)
    {
        $parts = imap_mime_header_decode($enc);
        $str='';
        for ($p=0; $pcharset;
            $part = $parts[$p]->text;
            if ($ch!=='default')
                $str.=mb_convert_encoding($part,'UTF-8',$ch);
            elseif ($ch=='default')
                $str.=mb_convert_encoding($part,'UTF-8','KOI8-R');

            else $str.=$part;
        }
        return $str;
    }

    private function getmsg($mid)
    {
        $this->htmlmsg = $this->plainmsg =  $this->charset = '';
        $this->attachments = array();

        $s = imap_fetchstructure($this->mbox,$mid);


        if (!isset($s->parts))
            $this->getpart($mid,$s,0);
        else
        {
            foreach ($s->parts as $partno0=>$p)
                $this->getpart($mid,$p,$partno0+1);
        }
    }

    private function getpart($mid,$p,$partno)
    {
        $data = ($partno) ? imap_fetchbody($this->mbox,$mid,$partno): imap_body($this->mbox,$mid);


        if ($p->encoding==4)
            $data = quoted_printable_decode($data);
        elseif ($p->encoding==3)
            $data = base64_decode($data);

        $params = array();

        if (isset($p->parameters))
            foreach ($p->parameters as $x)
                $params[ strtolower( $x->attribute ) ] = $x->value;

        if (isset($p->dparameters))
            foreach ($p->dparameters as $x)
                $params[ strtolower( $x->attribute ) ] = $x->value;


        if (isset($params['filename']) || isset($params['name']))
        {
            $filename = (isset($params['filename']))? $params['filename'] : $params['name'];
            if(isset($this->attachments[$filename])) //если уже есть в атачменте такой файл, делаем "версионность"
            {
                $this->filescount ++;
                $filename = $this->filescount."_".$filename;
            }
            $this->attachments[$filename] = $data;
        }
        elseif ($p->type==0 && isset($data))
        {

            if (strtolower($p->subtype)=='plain')
            {
                $this->plainmsg .= trim($this->ConvBody($data)) ."\n\n";
            }
            else
                $this->htmlmsg .= $this->ConvBody($data) ."

"; if(isset($params['charset'])) $this->charset = $params['charset']; } elseif ($p->type==2 && isset($data)) { $this->plainmsg .= trim($this->ConvBody($data)) ."\n\n"; } if (isset($p->parts)) { foreach ($p->parts as $partno0=>$p2) $this->getpart($mid,$p2,$partno.'.'.($partno0+1)); } } private function ConvBody($text) { if(!empty($this->hcharset) && $this->hcharset !='UTF-8') { $text = iconv($this->hcharset, 'UTF-8', $text); } return $text; } /** * Закрыть имап подключение */ public function close() { imap_close($this->mbox); } } //TODO: отловить момент, когда делают рассылку и данный пользователь так же учавствует в ней, выдает адрес, перввый из списка [to]


подключение и юзание:
require "inc/imap.php";
$options = array('search'=>'SINCE "'.date("j F Y",strtotime("-1 DAY")).'"');//тип поиска сообщений на почте - собираем сообщения со вчера по седня :)
$rm = new ImapCheck("{server.ru/imap}","mail@server.ru","password",$options);
$mail_list = $rm->getmail(); //получили письма
$rm->close();
выплюнет массив с письмами примерно так: [from] =>откуда [to] => кому [name] => имя приславшего [subject] => название [Date] => дата, письма [charset] => кодировка [plain] => если обычный текст [html] => если хтмл-код [letterID] => id письма на сервере (не постоянно! по факту это просто количественный номер) [attach] => Array ( [название_изображения] => бинарное содержание(то есть можно обычным fput воткнуть куда надо) )
да, кстати,есть небольшая бага: в некоторых случаях название атачей будет типа =?UTF_8?B?..... лечится просто :
iconv_mime_decode("название файла",0, "кодировка")
надеюсь, кому-то помог.

Комментариев нет:

Отправить комментарий