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

source: OpenPNE/branches/stable-2.12.x/webapp/lib/OpenPNE/KtaiMail.php @ 6931

Last change on this file since 6931 was 6931, checked in by ebihara, 14 years ago

#2030:日記・トピックコメントを添付画像付きでメール投稿する際に、ファイルサイズがIMAGE_MAX_FILESIZEを超過しているか、GIF,JPEG,PNGに正しく変換できない場合にエラーメールを送信して日記・トピックコメントへの投稿をおこなわないようにした

File size: 11.9 KB
Line 
1<?php
2/**
3 * @copyright 2005-2008 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                $item = $this->_get_images($part);
184                if ($item === false) {
185                    return false;
186                }
187
188                $images = array_merge($images, $item);
189            }
190        } elseif (strtolower($mail->ctype_primary) === 'image' &&
191                  in_array(strtolower($mail->ctype_secondary), $allowed_type)) {
192            $image_data = $mail->body;
193            $image_ext = '';
194
195             // 画像かどうかチェック
196            if (!@imagecreatefromstring($image_data)) {
197                // base64_decodeしてリトライ
198                $image_data = base64_decode($image_data);
199                if (!@imagecreatefromstring($image_data)) {
200                    return false;
201                }
202            }
203
204            // 一時ファイルを作成
205            $tmpfname = tempnam($this->img_tmp_dir, $this->img_tmp_prefix);
206
207            $fp = fopen($tmpfname, 'wb');
208            fwrite($fp, $image_data);
209            fclose($fp);
210
211            // 画像サイズのチェック
212            if ($this->img_max_filesize && filesize($tmpfname) > $this->img_max_filesize) {
213                unlink($tmpfname);
214                return false;
215            }
216
217            // 画像のリサイズ
218            list($width, $height, $type, $attr) = @getimagesize($tmpfname);
219            $need_resize = false;
220            $original_width = $width;
221            $original_height = $height;
222            //横のサイズが、指定されたサイズより大きい場合
223            if (IMAGE_MAX_WIDTH && ($width > IMAGE_MAX_WIDTH)) {
224                $need_resize = true;
225                $height = $height * (IMAGE_MAX_WIDTH / $width);
226                $width = IMAGE_MAX_WIDTH;
227            }
228            //縦サイズが、指定されたサイズより大きい場合
229            if (IMAGE_MAX_HEIGHT && ($height > IMAGE_MAX_HEIGHT)) {
230                $need_resize = true;
231                $width = $width * (IMAGE_MAX_HEIGHT / $height);
232                $height = IMAGE_MAX_HEIGHT;
233            }
234            if ($height < 1.) {
235                $height = 1;
236            }
237            if ($width < 1.) {
238                $width = 1;
239            }
240            if ($need_resize) {
241                resize_image($type, $tmpfname, $tmpfname, $original_width, $original_height, $width, $height);
242                $fp = fopen($tmpfname, 'rb');
243                $image_data = fread($fp, filesize($tmpfname));  // 一時ファイルを再度読み込み
244                fclose($fp);
245            }
246
247            // 画像が正しいかどうかチェック
248            switch (strtolower($mail->ctype_secondary)) {
249            case 'jpeg':
250                if (!@imagecreatefromjpeg($tmpfname)) {
251                    $image_data = '';
252                } else {
253                    $image_ext = 'jpg';
254                }
255                break;
256            case 'gif':
257                if (!@imagecreatefromgif($tmpfname)) {
258                    $image_data = '';
259                } else {
260                    $image_ext = 'gif';
261                }
262                break;
263            case 'png':
264                if (!@imagecreatefrompng($tmpfname)) {
265                    $image_data = '';
266                } else {
267                    $image_ext = 'png';
268                }
269                break;
270            }
271            unlink($tmpfname);
272
273            if ($image_data && $image_ext) {
274                $images = array(array('data' => $image_data, 'ext' => $image_ext));
275            }
276        }
277
278        return $images;
279    }
280
281    /**
282     * 文字列からメールアドレスを抽出
283     *
284     * @access private
285     * @param string $str 入力文字列
286     * @return string メールアドレス
287     */
288    function _get_mail_address($str)
289    {
290        if (!$str) {
291            return false;
292        }
293
294        // "(ダブルクォーテーション)を取り除く
295        // "example"@docomo.ne.jp
296        $str = str_replace('"', '', $str);
297
298        // <example@docomo.ne.jp> というメールアドレスになることがある。
299        //   日本語 <example@docomo.ne.jp>
300        // のような場合に複数マッチする可能性があるので、
301        // マッチした最後のものを取ってくるように変更
302        $matches = array();
303        $regx = '/([\.\w!#$%&\'*+\-\/=?^`{|}~]+@[\w!#$%&\'*+\-\/=?^`{|}~]+(\.[\w!#$%&\'*+\-\/=?^`{|}~]+)*)/';
304        if (preg_match_all($regx, $str, $matches)) {
305            return array_pop($matches[1]);
306        }
307
308        return false;
309    }
310
311    function convert_text_core($str)
312    {
313        $result = "";
314
315        for ($i = 0; $i < strlen($str); $i++) {
316            $c = 0;
317            $c1 = ord($str[$i]);
318            $c2 = ord($str[$i]);
319
320            // E-mail用絵文字から携帯用絵文字に変換
321            if ($c1 == 0xED || $c1 ==0xEE) {  // [e:358] ~ [e:499]、[e:700]~
322                $c = hexdec(bin2hex(substr($str, $i, 2))) + 1536;
323            } elseif ($c1 == 0xEB || $c1 == 0xEC) {  // [e:1]~[e:357]、[e:500]~[e:518]
324                $c = hexdec(bin2hex(substr($str, $i, 2))) + 2816;
325            }
326
327            if ($c) {
328                $bin = array();
329                $bin[0] = chr($c >> 8);
330                $bin[1] = chr($c - ($bin[0] << 8));
331                $emoji = emoji_escape_e($bin);
332                $result .= $emoji;
333                $i++;
334            } else {
335                $result .= $str[$i];
336                if ((0x81 <= $c1 && $c1 <= 0x9F) || 0xE0 <= $c1) {
337                    $result .= $str[$i+1];
338                    $i++;
339                }
340            }
341        }
342
343        return $result;
344    }
345
346    /**
347     * 文字エンコーディングの変換、空白文字の削除
348     *
349     * @access public
350     * @param string $str 変換前の文字列
351     * @param string $from_encoding
352     * @param string $to_encoding
353     * @return string 変換後の文字列
354     */
355    function convert_text($str, $from_encoding = '', $to_encoding = '')
356    {
357        if (!$from_encoding) {
358            $from_encoding = $this->from_encoding;
359        }
360
361        if (!$to_encoding) {
362            $to_encoding = $this->to_encoding;
363        }
364
365        $from_addr = explode('@', $this->get_from());
366        $domain = array_pop($from_addr);
367
368        if ($domain == 'ezweb.ne.jp') {  // auは絵文字変換もおこなう
369            // 絵文字変換をするため、いったんShift_JISに変換
370            $str = mb_convert_encoding($str, 'SJIS-win', $from_encoding);
371
372            // 絵文字変換
373            $str = $this->convert_text_core($str);
374
375            // 文字エンコーディング変換
376            $str = mb_convert_encoding($str, $to_encoding, 'SJIS-win');
377        } else {
378            $str = mb_convert_encoding($str, $to_encoding, $from_encoding);
379        }
380
381        // 空白文字の削除
382        $str = str_replace("\0", '', $str);
383        if ($this->trim_doublebyte_space) {
384            $str = mb_ereg_replace('([\s ])+$', '', $str);
385        } else {
386            $str = rtrim($str);
387        }
388
389        return $str;
390    }
391}
392
393?>
Note: See TracBrowser for help on using the repository browser.