ここの情報は古いです。ご理解頂いた上でお取り扱いください。

source: OpenPNE/trunk/webapp/lib/OpenPNE/KtaiMail.php @ 4867

Last change on this file since 4867 was 4867, checked in by ebihara, 13 years ago

#1711:mb_substitute_characterを使わない形に修正

File size: 11.8 KB
Line 
1<?php
2/**
3 * @copyright 2005-2007 OpenPNE Project
4 * @license   http://www.php.net/license/3_01.txt PHP License 3.01
5 * @author Ogawa ogawa@tejimaya.com
6 * @author Masaki Miyashita miyasita@zenzy.net
7 */
8
9// PEAR::Mail_mimeDecode
10require_once 'Mail/mimeDecode.php';
11require_once 'util.inc.php';
12
13/**
14 * OpenPNE_KtaiMail
15 * 携帯メール用のメールデコーダ
16 */
17class OpenPNE_KtaiMail
18{
19    /**
20     * デコード結果
21     * @var stdClass
22     */ 
23    var $mail;
24
25    /** @var string 変換元文字エンコーディング(デフォルト値) */
26    var $from_encoding = 'auto';
27
28    /** @var string 変換先文字エンコーディング */
29    var $to_encoding = 'UTF-8';
30
31    /** @var string 画像検証用のテンポラリディレクトリ */
32    var $img_tmp_dir;
33
34    /** @var string 画像検証用のテンポラリファイルの接頭辞 */
35    var $img_tmp_prefix = 'OpenPNE_KtaiMail_';
36
37    /** @var string 画像の最大ファイルサイズ(byte) */
38    var $img_max_filesize;
39
40    /** @var bool 全角スペースを削除するかどうか */
41    var $trim_doublebyte_space = true;
42
43    /**
44     * constructor
45     *
46     * @access public
47     * @param string $options
48     *      - from_encoding    : 変換元文字エンコーディング
49     *      - to_encoding      : 変換先文字エンコーディング
50     *      - img_tmp_dir      : 画像検証用のテンポラリディレクトリ
51     *      - img_tmp_prefix   : 画像検証用のテンポラリファイルの接頭辞
52     *      - img_max_filesize : 画像の最大ファイルサイズ
53     *      - trim_doublebyte_space : 全角スペースを削除するかどうか
54     */
55    function OpenPNE_KtaiMail($options = array())
56    {
57        foreach ($options as $key => $value) {
58            switch ($key) {
59            case 'from_encoding':
60                $this->from_encoding = $value;
61                break;
62            case 'to_encoding':
63                $this->to_encoding = $value;
64                break;
65            case 'img_tmp_dir':
66                $this->img_tmp_dir = $value;
67                break;
68            case 'img_tmp_prefix':
69                $this->img_tmp_prefix = $value;
70                break;
71            case 'img_max_filesize':
72                $this->img_max_filesize = $value;
73                break;
74            case 'trim_doublebyte_space':
75                $this->trim_doublebyte_space = (bool)$value;
76                break;
77            }
78        }
79    }
80
81    /**
82     * メールをデコード
83     *
84     * @access public
85     * @param string メールの生データ
86     */
87    function decode($raw_mail)
88    {
89        $params['include_bodies'] = true;
90        $params['decode_bodies']  = true;
91        $params['decode_headers'] = true;
92        $params['input'] = $raw_mail;
93
94        $this->mail =& Mail_mimeDecode::decode($params);
95    }
96
97    /**
98     * ヘッダー(From:)から送信元メールアドレスを取得
99     *
100     * @access public
101     * @return string Fromメールアドレス
102     */
103    function get_from()
104    {
105        return $this->_get_mail_address($this->mail->headers['from']);
106    }
107
108    /**
109     * ヘッダー(To:)から宛先メールアドレスを取得
110     *
111     * @access public
112     * @return string Toメールアドレス
113     */
114    function get_to()
115    {
116        return $this->_get_mail_address($this->mail->headers['to']);
117    }
118
119    /**
120     * Subject の内容を抽出(+文字コード変換)
121     *
122     * @access public
123     * @return string Subject
124     */
125    function get_subject()
126    {
127        return isset($this->mail->headers['subject']) ?
128                    $this->convert_text($this->mail->headers['subject']) : '';
129    }
130
131    /**
132     * メール本文からテキストを抽出(+文字コード変換)
133     *
134     * @access public
135     * @return string メール本文のテキスト
136     */
137    function get_text_body()
138    {
139        return $this->_get_text_body($this->mail);
140    }
141
142    /** @access private */
143    function _get_text_body(&$mail)
144    {
145        $body = '';
146
147        if (isset($mail->parts) && is_array($mail->parts)) {
148            // multipart
149            foreach ($mail->parts as $part) {
150                if ($body = $this->_get_text_body($part)) break;
151            }
152        } elseif (strtolower($mail->ctype_primary) === 'text' &&
153                  strtolower($mail->ctype_secondary) === 'plain') {
154            $body = $mail->body;
155            $charset = $mail->ctype_parameters['charset'];
156
157            $body = $this->convert_text($body, $charset);
158        }
159
160        return $body;
161    }
162
163    /**
164     * メールから画像データを抽出
165     *
166     * @access public
167     * @return array 画像データ配列
168     */
169    function get_images()
170    {
171        return $this->_get_images($this->mail);
172    }
173
174    /** @access private */
175    function _get_images(&$mail)
176    {
177        $allowed_type = array('jpeg', 'gif', 'png');
178        $images = array();
179
180        if (isset($mail->parts) && is_array($mail->parts)) {
181            // multipart
182            foreach ($mail->parts as $part) {
183                $images = array_merge($images, $this->_get_images($part));
184            }
185        } elseif (strtolower($mail->ctype_primary) === 'image' &&
186                  in_array(strtolower($mail->ctype_secondary), $allowed_type)) {
187            $image_data = $mail->body;
188            $image_ext = '';
189
190             // 画像かどうかチェック
191            if (!@imagecreatefromstring($image_data)) {
192                // base64_decodeしてリトライ
193                $image_data = base64_decode($image_data);
194                if (!@imagecreatefromstring($image_data)) {
195                    return array();
196                }
197            }
198
199            // 一時ファイルを作成
200            $tmpfname = tempnam($this->img_tmp_dir, $this->img_tmp_prefix);
201
202            $fp = fopen($tmpfname, 'wb');
203            fwrite($fp, $image_data);
204            fclose($fp);
205
206            // 画像サイズのチェック
207            if ($this->img_max_filesize && filesize($tmpfname) > $this->img_max_filesize) {
208                unlink($tmpfname);
209                return array();
210            }
211
212            // 画像のリサイズ
213            list($width, $height, $type, $attr) = @getimagesize($tmpfname);
214            $need_resize = false;
215            $original_width = $width;
216            $original_height = $height;
217            //横のサイズが、指定されたサイズより大きい場合
218            if (IMAGE_MAX_WIDTH && ($width > IMAGE_MAX_WIDTH)) {
219                $need_resize = true;
220                $height = $height * (IMAGE_MAX_WIDTH / $width);
221                $width = IMAGE_MAX_WIDTH;
222            }
223            //縦サイズが、指定されたサイズより大きい場合
224            if (IMAGE_MAX_HEIGHT && ($height > IMAGE_MAX_HEIGHT)) {
225                $need_resize = true;
226                $width = $width * (IMAGE_MAX_HEIGHT / $height);
227                $height = IMAGE_MAX_HEIGHT;
228            }
229            if ($height < 1.) {
230                $height = 1;
231            }
232            if ($width < 1.) {
233                $width = 1;
234            }
235            if ($need_resize) {
236                resize_image($type, $tmpfname, $tmpfname, $original_width, $original_height, $width, $height);
237                $fp = fopen($tmpfname, 'rb');
238                $image_data = fread($fp, filesize($tmpfname));  // 一時ファイルを再度読み込み
239                fclose($fp);
240            }
241
242            // 画像が正しいかどうかチェック
243            switch (strtolower($mail->ctype_secondary)) {
244            case 'jpeg':
245                if (!@imagecreatefromjpeg($tmpfname)) {
246                    $image_data = '';
247                } else {
248                    $image_ext = 'jpg';
249                }
250                break;
251            case 'gif':
252                if (!@imagecreatefromgif($tmpfname)) {
253                    $image_data = '';
254                } else {
255                    $image_ext = 'gif';
256                }
257                break;
258            case 'png':
259                if (!@imagecreatefrompng($tmpfname)) {
260                    $image_data = '';
261                } else {
262                    $image_ext = 'png';
263                }
264                break;
265            }
266            unlink($tmpfname);
267
268            if ($image_data && $image_ext) {
269                $images = array(array('data' => $image_data, 'ext' => $image_ext));
270            }
271        }
272
273        return $images;
274    }
275
276    /**
277     * 文字列からメールアドレスを抽出
278     *
279     * @access private
280     * @param string $str 入力文字列
281     * @return string メールアドレス
282     */
283    function _get_mail_address($str)
284    {
285        if (!$str) {
286            return false;
287        }
288
289        // "(ダブルクォーテーション)を取り除く
290        // "example"@docomo.ne.jp
291        $str = str_replace('"', '', $str);
292
293        // <example@docomo.ne.jp> というメールアドレスになることがある。
294        //   日本語 <example@docomo.ne.jp>
295        // のような場合に複数マッチする可能性があるので、
296        // マッチした最後のものを取ってくるように変更
297        $matches = array();
298        $regx = '/([\.\w!#$%&\'*+\-\/=?^`{|}~]+@[\w!#$%&\'*+\-\/=?^`{|}~]+(\.[\w!#$%&\'*+\-\/=?^`{|}~]+)*)/';
299        if (preg_match_all($regx, $str, $matches)) {
300            return array_pop($matches[1]);
301        }
302
303        return false;
304    }
305
306    function convert_text_core($str)
307    {
308        $result = "";
309
310        for ($i = 0; $i < strlen($str); $i++) {
311            $c = 0;
312            $c1 = ord($str[$i]);
313            $c2 = ord($str[$i]);
314
315            // E-mail用絵文字から携帯用絵文字に変換
316            if ($c1 == 0xED || $c1 ==0xEE) {  // [e:358] ~ [e:499]、[e:700]~
317                $c = hexdec(bin2hex(substr($str, $i, 2))) + 1536;
318            } elseif ($c1 == 0xEB || $c1 == 0xEC) {  // [e:1]~[e:357]、[e:500]~[e:518]
319                $c = hexdec(bin2hex(substr($str, $i, 2))) + 2816;
320            }
321
322            if ($c) {
323                $bin = array();
324                $bin[0] = chr($c >> 8);
325                $bin[1] = chr($c - ($bin[0] << 8));
326                $emoji = emoji_escape_e($bin);
327                $result .= $emoji;
328                $i++;
329            } else {
330                $result .= $str[$i];
331                if ((0x81 <= $c1 && $c1 <= 0x9F) || 0xE0 <= $c1) {
332                    $result .= $str[$i+1];
333                    $i++;
334                }
335            }
336        }
337
338        return $result;
339    }
340 
341    /**
342     * 文字エンコーディングの変換、空白文字の削除
343     *
344     * @access public
345     * @param string $str 変換前の文字列
346     * @param string $from_encoding
347     * @param string $to_encoding
348     * @return string 変換後の文字列
349     */
350    function convert_text($str, $from_encoding = '', $to_encoding = '')
351    {
352        if (!$from_encoding) {
353            $from_encoding = $this->from_encoding;
354        }
355
356        if (!$to_encoding) {
357            $to_encoding = $this->to_encoding;
358        }
359
360        $from_addr = explode('@', $this->get_from());
361        $domain = array_pop($from_addr);
362
363        if ($domain == 'ezweb.ne.jp') {  // auは絵文字変換もおこなう
364            // 絵文字変換をするため、いったんShift_JISに変換
365            $str = mb_convert_encoding($str, 'SJIS-win', $from_encoding);
366
367            // 絵文字変換
368            $str = $this->convert_text_core($str);
369
370            // 文字エンコーディング変換
371            $str = mb_convert_encoding($str, $to_encoding, 'SJIS-win');
372        } else {
373            $str = mb_convert_encoding($str, $to_encoding, $from_encoding);
374        }
375
376        // 空白文字の削除
377        $str = str_replace("\0", '', $str);
378        if ($this->trim_doublebyte_space) {
379            $str = mb_ereg_replace('([\s ])+$', '', $str);
380        } else {
381            $str = rtrim($str);
382        }
383
384        return $str;
385    }
386}
387
388?>
Note: See TracBrowser for help on using the repository browser.