OSDN Git Service

git-svn-id: https://svn.sourceforge.jp/svnroot/nucleus-jp/plugin@1059 1ca29b6e-896d...
[nucleus-jp/nucleus-plugins.git] / trunk / NP_Moblog / NP_Moblog.php
1 <?php
2 // vim: tabstop=2:shiftwidth=2
3
4 /**
5   * NP_Moblog ($Revision: 1.1 $)
6   * by hsur ( http://blog.cles.jp/np_cles )
7   * $Id: NP_Moblog.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
8   *
9   * Based on NP_HeelloWorld v0.8 
10   * http://nakahara21.com/?itemid=133
11 */
12
13 /*
14   * Copyright (C) 2003 nakahara21 All rights reserved.
15   * Copyright (C) 2004-2007 cles All rights reserved.
16   *
17   * This program is free software; you can redistribute it and/or
18   * modify it under the terms of the GNU General Public License
19   * as published by the Free Software Foundation; either version 2
20   * of the License, or (at your option) any later version.
21   * 
22   * This program is distributed in the hope that it will be useful,
23   * but WITHOUT ANY WARRANTY; without even the implied warranty of
24   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25   * GNU General Public License for more details.
26   * 
27   * You should have received a copy of the GNU General Public License
28   * along with this program; if not, write to the Free Software
29   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
30   * 
31   * In addition, as a special exception, mamio and cles gives
32   * permission to link the code of this program with those files in the PEAR
33   * library that are licensed under the PHP License (or with modified versions
34   * of those files that use the same license as those files), and distribute
35   * linked combinations including the two. You must obey the GNU General Public
36   * License in all respects for all of the code used other than those files in
37   * the PEAR library that are licensed under the PHP License. If you modify
38   * this file, you may extend this exception to your version of the file,
39   * but you are not obligated to do so. If you do not wish to do so, delete
40   * this exception statement from your version.
41 */
42
43 global $DIR_LIBS;
44 require_once($DIR_LIBS . 'MEDIA.php');
45
46 // クラスのロード
47 require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
48 require_once('Net/POP3.php');
49 require_once('Mail/mimeDecode.php');
50 require_once('Mail/RFC822.php');
51
52 // バージョンチェック
53 $required = '4.3.0';
54 if( ! version_compare(phpversion() , $required , '>=') ){
55         ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
56 }
57
58 if (!function_exists('sql_table')) {
59         function sql_table($name) {
60                 return 'nucleus_'.$name;
61         }
62 }
63
64 class NP_Moblog extends NucleusPlugin {
65
66         // name of plugin
67         function getName() {
68                 return 'Moblog';
69         }
70
71         // author of plugin
72         function getAuthor() {
73                 return 'hsur';
74         }
75
76         // an URL to the plugin website
77         // can also be of the form mailto:foo@bar.com
78         function getURL() {
79                 return 'http://blog.cles.jp/np_cles/category/31/subcatid/2';
80         }
81
82         // version of the plugin
83         function getVersion() {
84                 return '1.16';
85         }
86         
87         function hasAdminArea() {
88                 return 1;
89         }
90         
91         function getEventList() {
92                 return array('PrePluginOptionsEdit');
93         }
94         
95         function event_PrePluginOptionsEdit(&$data) {
96                 switch($data['context']){
97                         case 'member':
98                                 // idandcat
99                                 $m =& MEMBER :: createFromID($data['contextid']);
100                                 $trimChar = array(
101                                         '=' => '',
102                                         '|' => '',
103                                         ';' => '',
104                                 );
105                                 
106                                 $blogs = Array();
107                                 $res = mysql_query('SELECT bnumber, bname FROM '.sql_table('blog'));
108                                 while( $o = mysql_fetch_object($res) ){
109                                          if( $m->isTeamMember($o->bnumber) ){
110                                                 $blogs[$o->bnumber] = $o->bname;
111                                          }
112                                 }
113                                 
114                                 $idandcatTypeInfo = '';
115                                 foreach($blogs as $blogid => $blogname){
116                                         $res = mysql_query('SELECT catid, cname FROM '.sql_table('category').' WHERE cblog='.$blogid);
117                                         if( @mysql_num_rows($res) > 0) {
118                                                 while( $o = mysql_fetch_object($res) ){
119                                                         if($idandcatTypeInfo)
120                                                                 $idandcatTypeInfo .= '|';
121                                                         $o->cname = strtr($o->cname, $trimChar);
122                                                         $blogname = strtr($blogname, $trimChar);
123                                                         $idandcatTypeInfo .= "{$o->cname} ({$blogname})|{$blogid},{$o->catid}";
124                                                 }
125                                         }
126                                 }
127                                 if( ! $idandcatTypeInfo ){
128                                         $idandcatTypeInfo = "!!投稿可能なblogがありません!!|0,0";
129                                 }
130                                 
131                                 // collection & thumb_col
132                                 $collections = MEDIA::getCollectionList();
133                                 $mid = intval($m->getID());
134                                 $collections[$mid] = 'デフォルト(useridディレクトリ)';
135                                 
136                                 $collectionTypeInfo = '';
137                                 foreach( $collections as $collection => $name ){
138                                         if($collectionTypeInfo) $collectionTypeInfo .= '|';
139                                         $name = strtr($name, $trimChar);
140                                         $collection = strtr($collection, $trimChar);
141                                         $collectionTypeInfo .= "{$name}|{$collection}";
142                                 }
143                                 
144                                 // set options
145                                 foreach($data['options'] as $oid => $option ){
146                                         switch($data['options'][$oid]['name']){
147                                                 case 'idandcat':
148                                                         $data['options'][$oid]['typeinfo'] = $idandcatTypeInfo;
149                                                         break;
150                                                 case 'collection':
151                                                 case 'thumb_col':
152                                                         $data['options'][$oid]['typeinfo'] = $collectionTypeInfo;
153                                                         break;
154                                         }
155                                 }
156                                                                 
157                                 break;
158                         default:
159                                 // nothing      
160                 }
161         }
162         
163         function install() {
164                 $this->createMemberOption('enable', 'プラグインを有効にするか?', 'yesno', 'no');
165
166                 $this->createMemberOption('host', 'POP3 ホスト名', 'text', 'localhost');
167                 $this->createMemberOption('port', 'POP3 ポート', 'text', '110', 'numerical=true');
168                 $this->createMemberOption('user', 'POP3 ユーザー名', 'text', '');
169                 $this->createMemberOption('pass', 'POP3 パスワード', 'password', '');
170                 $this->createMemberOption('useAPOP', 'APOPを使用するか?', 'yesno', 'no');
171
172                 $this->createMemberOption('idandcat', 'Nucleusカテゴリ(Blog)', 'select', '', '');
173
174                 $this->createMemberOption('collection', '画像を保存するディレクトリ', 'select', '', '');
175                 $this->createMemberOption('thumb_col', 'サムネイルをを保存するディレクトリ', 'select', '', '');
176
177                 $this->createMemberOption('imgonly', 'イメージ添付メールのみ追加?', 'yesno', 'no');
178                 $this->createMemberOption('DefaultPublish', 'デフォルトで公開するか?', 'yesno', 'no');
179
180                 $this->createMemberOption('optionsKeyword', 'オプション記述開始の区切り文字', 'text', '@');
181                 $this->createMemberOption('blogKeyword', 'オプションでblogidを指定する場合のキー', 'text', 'b');
182                 $this->createMemberOption('categoryKeyword', 'オプションでカテゴリを指定する場合のキー', 'text', 'c');
183                 $this->createMemberOption('publishKeyword', 'オプションでストレートにpublish指定する場合のキー', 'text', 's');
184
185                 $this->createMemberOption('moreDelimiter', '追記にする場合の区切り文字(利用しない場合は空欄)', 'text', '');
186
187                 $this->createMemberOption('accept', '投稿許可アドレス(複数の場合改行で区切ってください)', 'textarea', '');
188                 $this->createMemberOption('acceptSubjectPrefix','投稿許可SubjectPrefix(制限無しの場合は空欄)','text','');
189
190                 $this->createMemberOption('nosubject', '件名がないときの題名', 'text', '');
191                 $this->createMemberOption('no_strip_tags', 'htmlメールの場合に除去しないタグ', 'textarea', '<title><hr><h1><h2><h3><h4><h5><h6><div><p><pre><sup><ul><ol><br><dl><dt><table><caption><tr><li><dd><th><td><a><area><img><form><input><textarea><button><select><option>');
192                 $this->createMemberOption('maxbyte', '最大添付量(B)', 'text', '300000', 'numerical=true');
193                 $this->createMemberOption('subtype', '対応MIMEタイプ(正規表現)', 'text', 'gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf');
194                 $this->createMemberOption('viri', '保存しないファイル(正規表現)', 'text', '.+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$');
195                 $this->createMemberOption('imgExt', '画像ファイルの拡張子(正規表現)', 'text', '.+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$');
196
197                 $this->createMemberOption('thumb_ok', 'サムネイルを使用する?', 'yesno', 'yes');
198                 $this->createMemberOption('W', 'サムネイルの大きさ(Width)', 'text', '120', 'numerical=true');
199                 $this->createMemberOption('H', 'サムネイルの大きさ(Hight)', 'text', '120', 'numerical=true');
200                 $this->createMemberOption('thumb_ext', 'サムネイルを作る対象画像', 'text', '.+\.jpe?g$|.+\.png$');
201                 $this->createMemberOption('smallW', 'アイテム内に表示する画像の最大横幅', 'text', '120', 'numerical=true');
202
203                 $this->createMemberOption('textTpl', 'テキストテンプレート', 'textarea', '<%body%>');
204                 $this->createMemberOption('withThumbTpl', 'サムネイル付きテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%thumbUrl%>|<%thumbW%>|<%thumbH%>|)%></a></div><%body%>');
205                 $this->createMemberOption('withoutThumbTpl', 'サムネイルなしテンプレート', 'textarea', '<div class="leftbox"><%image(<%imageUrl%>|<%sizeW%>|<%sizeH%>|)%></div><%body%>');
206                 $this->createMemberOption('reductionTpl', 'サムネイルなしテンプレート(縮小)', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%imageUrl%>|<%reductionW%>|<%reductionH%>|)%></a></div><%body%>');
207                 $this->createMemberOption('dataTpl', 'データファイルテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%fileName%></a></div><%body%>');
208                 
209                 $this->createOption('execMode', '動作モード', 'select', '1', '振分対応モード|0|互換モード|1');
210                 $this->createOption('spamCheck', 'SPAMチェックを有効にする', 'yesno', 'no');
211                 
212                 $this->createOption('interval', 'メール取得の間隔(秒)', 'text', '600', 'numerical=true');
213                 $this->createOption('nextUpdate', '次回更新時刻(変更できません)', 'text', '-', 'access=readonly');
214                 $this->createOption('lastUpdate', '最終更新時刻(UnixTimeStamp,)', 'text', '0', 'access=hidden');
215                 $this->createOption('debug', 'ログを出力を行うか?', 'yesno', 'no');
216         }
217         
218         function unInstall() {}
219         function getMinNucleusVersion() { return 320; }
220         function getMinNucleusPatchLevel() { return 0; }
221
222         // a description to be shown on the installed plugins listing
223         function getDescription() {
224                 return '[$Revision: 1.1 $]<br />メールを拾ってアイテムを追加します。&lt;%Moblog%&gt;の記述のあるスキンを適用するページを開くと実行されます。<br />
225                                 &lt;%Moblog(link)%&gt;と記入することでメールを取得するためのリンクを表示することができます(要ログイン)<br />
226                                 個人ごとに設定ができるようになりましたので「あなたの設定」か「メンバー管理」から設定を行ってください。';
227         }
228
229         function supportsFeature($what) {
230                 switch ($what) {
231                         case 'SqlTablePrefix' :
232                         case 'HelpPage':
233                                 return 1;
234                         default :
235                                 return 0;
236                 }
237         }
238
239         function _info($msg) {
240                 if ($this->getOption('debug') == 'yes') {
241                         ACTIONLOG :: add(INFO, 'Moblog: '.$msg);
242                 }
243         }
244
245         function _warn($msg) {
246                 ACTIONLOG :: add(WARNING, 'Moblog: '.$msg);
247         }
248
249         function _getEnableUserId() {
250                 $userOptions = $this->getAllMemberOptions('enable');
251                 $userIds = Array ();
252                 foreach( $userOptions as $userId => $value ){
253                         if( $value == 'yes') $userIds[] = $userId;
254                 }
255                 return $userIds;
256         }
257
258         function _initMediaDirByUserId($userId) {
259                 global $DIR_MEDIA;
260
261                 $this->_info(__LINE__ . ": ユーザ($userId)の初期設定");
262                 $this->memid = $userId;
263
264                 /*-- 受信メールサーバーの設定--*/
265                 $this->host = $this->getMemberOption($userId, 'host');
266                 $this->port = $this->getMemberOption($userId, 'port');
267                 $this->user = $this->getMemberOption($userId, 'user');
268                 $this->pass = $this->getMemberOption($userId, 'pass');  
269
270                 // メールでアイテムを追加するblogのID
271                 $idandcat = $this->getMemberOption($userId, 'idandcat');
272                 list($this->blogid, $this->categoryNameOrId) = explode(",", $idandcat);
273                 
274                 $this->imgonly = ($this->getMemberOption($userId, 'imgonly') == 'yes') ? 1 : 0;
275                 $this->DefaultPublish = ($this->getMemberOption($userId, 'DefaultPublish') == 'yes') ? 1 : 0;
276
277                 /*-- メールのタイトルに各種オプションを含める場合の設定--*/
278                 $this->optionsKeyword = $this->getMemberOption($userId, 'optionsKeyword');
279                 $this->blogKeyword = $this->getMemberOption($userId, 'blogKeyword');
280                 $this->categoryKeyword = $this->getMemberOption($userId, 'categoryKeyword');
281                 $this->publishKeyword = $this->getMemberOption($userId, 'publishKeyword');
282
283                 // 投稿許可アドレス
284                 $this->accept = explode("\n", $this->getMemberOption($userId, 'accept'));
285                 $this->accept = Array_Map("Trim", $this->accept);
286                 $this->accept = Array_Map("strtolower", $this->accept);
287                 foreach( $this->accept as $mailAddr ){
288                         $this->_info(__LINE__ . "許可アドレス user:$userId, $mailAddr");
289                 }               
290
291                 // 投稿許可SubjectPrefix
292                 $this->acceptSubjectPrefix = Trim($this->getMemberOption($userId, 'acceptSubjectPrefix'));
293                 
294                 // 追記の区切り
295                 $this->moreDelimiter = Trim($this->getMemberOption($userId, 'moreDelimiter'));
296                 
297                 // 件名がないときの題名
298                 $this->nosubject = $this->getMemberOption($userId, 'nosubject');
299                 $this->no_strip_tags = $this->getMemberOption($userId, 'no_strip_tags');
300
301                 // 最大添付量(バイト・1ファイルにつき)※超えるものは保存しない
302                 $this->maxbyte = $this->getMemberOption($userId, 'maxbyte');
303                 $this->subtype = $this->getMemberOption($userId, 'subtype');
304                 $this->viri = $this->getMemberOption($userId, 'viri');
305                 $this->imgExt = $this->getMemberOption($userId, 'imgExt');
306
307                 // サムネイル
308                 $this->thumb_ok = ($this->getMemberOption($userId, 'thumb_ok') == 'yes') ? 1 : 0;
309                 $this->W = $this->getMemberOption($userId, 'W');
310                 $this->H = $this->getMemberOption($userId, 'H');
311                 $this->thumb_ext = $this->getMemberOption($userId, 'thumb_ext');
312                 $this->smallW = $this->getMemberOption($userId, 'smallW');
313                 
314                 // 画像保存ディレクトリ
315                 $collections = MEDIA::getCollectionList();
316                 $collection = $this->getMemberOption($userId, 'collection');
317                 if( $collection && isset($collections[$collection]) ){
318                         $this->collection = $collection;
319                 } else {
320                         $this->_info(__LINE__ . ": 画像保存ディレクトリが正しくありません。デフォルトを使用します");
321                         $this->collection = $this->memid;
322                 }
323                 
324                 $this->tmpdir = $DIR_MEDIA.$this->collection.'/';
325                 $this->_info(__LINE__ . ": 画像保存ディレクトリ: $this->tmpdir");
326                 
327                 if (!@is_dir($this->tmpdir)) {
328                         $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないので、ディレクトリを作成します。");
329                         $oldumask = umask(0000);
330                         if (!@mkdir($this->tmpdir, 0777))
331                                 return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
332                         umask($oldumask);                               
333                 } 
334
335                 if (!is_writable($this->tmpdir)) {
336                         $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないか、書き込み可能になっていません");
337                 }
338                 
339                 // サムネイル保存ディレクトリ
340                 $thumb_collection = $this->getMemberOption($userId, 'thumb_col');
341                 if( $collection && isset($collections[$thumb_collection]) ){
342                         $this->thumb_collection = $thumb_collection;
343                 } else {
344                         $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリが正しくありません。デフォルトを使用します");
345                         $this->thumb_collection = $this->memid;
346                 }
347
348                 $this->thumb_dir = $DIR_MEDIA.$this->thumb_collection.'/';
349                 $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリ: $this->thumb_dir");
350                 
351                 if (!@is_dir($this->thumb_dir)) {
352                 $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないので、ディレクトリを作成します。");
353                         $oldumask = umask(0000);
354                         if (!@mkdir($this->thumb_dir, 0777))
355                                 return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
356                         umask($oldumask);                               
357                 } 
358                 
359                 if (!is_writable($this->thumb_dir)) {
360                         $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないか、書き込み可能になっていません");
361                 }
362         }
363
364         function _convert($str, $input_encoding = false) {
365                 if( ! $input_encoding ){
366                         $input_encoding = "ISO-2022-JP,ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-2022-JP";
367                         $encoding = mb_detect_encoding($input_encoding);
368                         if( ! $encoding )
369                                 $input_encoding = "ISO-2022-JP";
370                 }
371                 return mb_convert_encoding($str, _CHARSET, $input_encoding);
372         }
373         
374         function _addr_search($str) {
375                 if( PEAR::isError($addresses = Mail_RFC822::parseAddressList($str)) ){
376                         return false;
377                 }
378                 $addr = array_shift($addresses);
379                 if($addr)
380                         return $addr->mailbox . "@" .$addr->host;
381                 return false;
382         }
383
384         function _thumb_create($src, $W, $H, $thumb_dir = "./") {
385                         // 画像の幅と高さとタイプを取得
386                                 $size = GetImageSize($src);
387                 switch ($size[2]) {
388                         case 1 :
389                                 return false;
390                                 break;
391                         case 2 :
392                                 $im_in = @ImageCreateFromJPEG($src);
393                                 break;
394                         case 3 :
395                                 $im_in = @ImageCreateFromPNG($src);
396                                 break;
397                 }
398                 if (!$im_in) {
399                         $this->_warn(__LINE__ . ": GDをサポートしていないか、ソースが見つかりません<br>phpinfo()でGDオプションを確認してください");
400                         return false;
401                 }
402                 // リサイズ
403                 if ($size[0] > $W || $size[1] > $H) {
404                         $key_w = $W / $size[0];
405                         $key_h = $H / $size[1];
406                         ($key_w < $key_h) ? $keys = $key_w : $keys = $key_h;
407                         $out_w = $size[0] * $keys;
408                         $out_h = $size[1] * $keys;
409                 } else {
410                         $out_w = $size[0];
411                         $out_h = $size[1];
412                 }
413                 // 出力画像(サムネイル)のイメージを作成し、元画像をコピーします。(GD2.0用)
414                 $im_out = ImageCreateTrueColor($out_w, $out_h);
415                 $resize = ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $size[0], $size[1]);
416
417                 // サムネイル画像をブラウザに出力、保存
418                 $filename = substr($src, strrpos($src, "/") + 1);
419                 ImageJPEG($im_out, $thumb_dir.$this->_getThumbFileName($filename)); //jpgサムネイル作成
420                 // 作成したイメージを破棄
421                 ImageDestroy($im_in);
422                 ImageDestroy($im_out);
423                 
424                 $this->_info(__LINE__ . ": サムネイルを作成しました" . $thumb_dir.$this->_getThumbFileName($filename));
425                 return true;
426         }
427         
428         function _getThumbFileName($filename){
429                 $filename = substr($filename, 0, strrpos($filename, "."));
430                 return $filename."-small.jpg";
431         }
432
433         function _getExecuteLink() {
434                 global $CONF;
435                 return $CONF['ActionURL'].'?action=plugin&amp;name=Moblog&amp;type=execute';
436         }
437
438         function _checkLastupdate() {
439                 $now = time();
440                 $lastUpdate = $this->getOption('lastUpdate');
441                 $interval = $this->getOption('interval');
442
443                 if ($lastUpdate + $interval < $now) {
444                         $this->setOption('lastUpdate', $now);
445                         $this->setOption('nextUpdate', date("Y-m-d H:i:s", $lastUpdate + $interval));
446                         $this->_info(__LINE__ . ": 更新します。");
447                         return true;
448                 }
449                 $this->_info(__LINE__ . ": 更新しません。次回更新は".date("Y-m-d H:i:s", $lastUpdate + $interval)."以降です。");
450                 return false;
451         }
452
453         function doSkinVar($skinType, $type = "") {
454                 global $member;
455                 switch ($type) {
456                         case '' :
457                         case 'execute' :
458                                 if ( $this->_checkLastupdate() )
459                                         $this->execute();
460                                 break;
461
462                         case 'link' :
463                                 if ( $member->isLoggedIn() )
464                                         echo '<a href="'.$this->_getExecuteLink().'">Add Item by Mail</a>';
465                                 break;
466                 }
467         } //end of function doSkinVar($skinType)
468
469         function doAction($type) {
470                 global $member;
471
472                 switch ($type) {
473                         case '' :
474                         case 'execute' :
475                                 if (!$member->isLoggedIn())
476                                         return "ログインが必要です";
477                                 $this->execute();
478                                 header('Location: ' . serverVar('HTTP_REFERER'));
479                                 break;
480
481                         default :
482                                 return 'アクションが定義されていません: '.$type;
483                 }
484         }
485
486         function execute() {
487                 $this->execMode = intval($this->getOption('execMode'));
488                 if( $this->execMode == 0 ){
489                         // false
490                         $this->_info(__LINE__ . ": 振分対応モードで動作します。");
491                 } elseif ( $this->execMode == 1 ) {
492                         // true
493                         $this->_info(__LINE__ . ": 互換モードで動作します");                 
494                 }
495                 
496                 $enabledUserIds = $this->_getEnableUserId();
497                 foreach ($enabledUserIds as $userId) {
498                         $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得開始します");
499                         $this->_initMediaDirByUserId($userId);
500                                                 
501                         // 接続
502                         $pop3 =& new Net_POP3();
503                         $pop3->_timeout = 10;
504                         
505                         if( ! $pop3->connect($this->host, $this->port) ){
506                                 $this->_warn(__LINE__ . ": POPサーバーに接続できません");
507                                 continue;
508                         }
509                         
510                         // 認証
511                         $authMethod = $this->getMemberOption($this->memid, 'useAPOP') == 'yes' ? 'APOP' : 'USER';
512                         $this->_info(__LINE__ . ": $authMethod で認証を行います");
513                 if(PEAR::isError($ret =& $pop3->login($this->user , $this->pass , $authMethod)) ){              
514                                 $this->_warn(__LINE__ . ": 認証に失敗しました:" . $ret->getMessage() );
515                                 $pop3->disconnect();
516                                 continue;
517                 }
518                         
519                         // 件数確認
520                         $num = $pop3->numMsg();
521                         $this->_info(__LINE__ . ": $num 件のメールがあります");
522                         if ($num == "0") {
523                                 $pop3->disconnect();
524                                 continue;
525                         }
526         
527                         // Msg取得
528                         for ($i = 1; $i <= $num; $i ++) {
529                                 if(! $msg =& $pop3->getMsg($i) ){
530                                         $this->_warn(__LINE__ . ": メールの取得に失敗しました。");
531                                 }
532                                 
533                                 $result = $this->addItemByMail($userId, $msg);
534                                 if( $result ){
535                                         $this->_info(__LINE__ . ": メッセージを削除します");
536                                         $pop3->deleteMsg($i);
537                                 }
538                         }
539                         //切断
540                         $pop3->disconnect();
541                         $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得終了しました");
542                 }
543         }
544
545         function addItemByMail($userId, $msg) {
546                 // メールデコード
547                 $params['include_bodies'] = TRUE;
548                 $params['decode_bodies']  = TRUE;
549                 $params['decode_headers'] = TRUE;
550                 $params['input'] = $msg;
551         if(PEAR::isError( $decodedMsg = Mail_mimeDecode::decode($params)) ){            
552                         $this->_warn(__LINE__ . ": メールデコードに失敗しました:" . $decodedMsg->getMessage() );
553                         return true;
554         }
555
556                 // From:
557                 if ( $decodedMsg->headers['from'] ) {
558                         $from = $this->_addr_search($decodedMsg->headers['from']);
559                 }
560                 if ( (! $from ) && $decodedMsg->headers['reply-to'] ) {
561                         $from = $this->_addr_search($decodedMsg->headers['reply-to']);
562                 }
563                 if ( (! $from ) && $decodedMsg->headers['return-path'] ) {
564                         $from = $this->_addr_search($decodedMsg->headers['return-path']);
565                 }
566                 if ( ! $from ){
567                         $this->_warn(__LINE__ . ": メールに送信者アドレスが見つかりません");
568                         return true;
569                 }
570                 $this->_info(__LINE__ . ": From($from)");
571
572                 // 投稿可能かチェック
573                 $from = strtolower(Trim($from));
574                 if ( in_array($from, $this->accept) ) {
575                         $this->_info(__LINE__ . ": 投稿許可アドレスに含まれているので受付($from)");
576                 } elseif( in_array( "*", $this->accept) ){
577                         $this->_info(__LINE__ . ": 投稿許可アドレスにワイルドカードが含まれているので受付($from)");
578                 }else {
579                         if( $this->execMode == 0 ){
580                                 $this->_info(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)。振り分け対応モードなので他のアカウントで取得が行われる場合があります。");
581                         } else {
582                                 $this->_warn(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)");
583                         }
584                         // 互換の場合は true
585                         // 振り分けの場合は false
586                         return $this->execMode;
587                 }
588
589                 // Date:
590                 $blog =& new BLOG($this->blogid);
591                 
592                 $timestamp = strtotime( trim($decodedMsg->headers['date']) );
593                 if ($timestamp == -1){
594                         $this->_info(__LINE__ . ": Dateヘッダからのtimestamp取得に失敗しました。");
595                         $timestamp = 0; 
596                 }
597                 $timestamp = $blog->getCorrectTime($timestamp);
598
599                 // Subject:
600                 $subject = $this->_convert($decodedMsg->headers['subject']);
601                 
602                 // Subject: prefixチェック                          
603                 if ( $this->acceptSubjectPrefix ){
604                         $this->_info(__LINE__ . ": 投稿許可SubjectPrefixがあります(prefix: $this->acceptSubjectPrefix)");      
605                         $pos = mb_strpos($subject, $this->acceptSubjectPrefix);
606                         if( $pos === 0 ){
607                                 // prefix切り取り
608                                 $subject = mb_substr($subject, mb_strlen($this->acceptSubjectPrefix) );
609                                 $this->_info(__LINE__ . ": 投稿許可SubjectPrefixをみつけました(prefix削除後subject: $subject)");                          
610                         } else {
611                                 $this->_warn(__LINE__ . ": 投稿許可SubjectPrefixがないので拒否します($subject)");
612                                 return true;
613                         }
614                 }
615                 
616                 // subject: オプション分割
617                 if (preg_match('/'.$this->optionsKeyword.'/i', $subject)) {
618                         list ($subject, $option) = spliti($this->optionsKeyword, $subject, 2);
619
620                         $this->_info(__LINE__ . ": Subject($subject), Option($option)");
621                                                 
622                         $option = '&'.$option;
623                         if (preg_match('/&' . $this->blogKeyword . '=([^&=]+)/i', $option, $word)) {
624                                 $this->blogid = $word[1];
625                                 $this->_info(__LINE__ . ': blogidを' . $this->blogid . 'で上書きします');
626                         }
627                         if (preg_match('/&' . $this->categoryKeyword . '=([^&=]+)/i', $option, $word)) {
628                                 $this->categoryNameOrId = $word[1];
629                                 $this->_info(__LINE__ . ': Categoryを' . $this->categoryNameOrId . 'で上書きします');
630                         }
631                         if (preg_match('/&' . $this->publishKeyword . '=([^&=]+)/i', $option, $word)) {
632                                 $this->DefaultPublish = $word[1];
633                                 $this->_info(__LINE__ . ($this->DefaultPublish ? ': 投稿を公開に上書きします' : ': 投稿を下書きに上書きします'));
634                         }
635                 }
636                 
637                 // Subject: 空の場合
638                 if( ! $subject = trim(htmlspecialchars($subject)) ){
639                         $subject = $this->nosubject;
640                 }
641
642                 // body
643                 $text = "";     
644                 if( strtolower($decodedMsg->ctype_primary) == "text" ){
645                         $this->_info(__LINE__ . ": single partメッセージです");
646                         $text = $this->_textPart($decodedMsg);
647                 } elseif ( strtolower($decodedMsg->ctype_primary) == "multipart" ){
648                         $this->_info(__LINE__ . ": multipart partメッセージです");
649                         $texts = Array();
650                         $fileNames = Array();                   
651                         $this->_decodeMultiPart($decodedMsg->parts, $texts, $fileNames);
652                         
653                         $text = $texts['plain'];
654                         if( $texts['html'] ) $text = $texts['html'];
655                 }
656                 
657                 if ($this->imgonly && (! $fileNames) ) {
658                         $this->_info(__LINE__ . ": 添付ファイルがないので書き込みません");
659                         return true;
660                 }
661
662                 if( $this->_isSpam($text) ){
663                         $this->_warn(__LINE__ . ": SPAMのため追加しません");
664                         return true;
665                 }
666                 
667                 $body = '';
668                 // body 生成
669                 if ( ! $fileNames ) {
670                         // 添付ファイルがない場合のbody                     
671                         $vars = array (
672                                 'body' => $text,
673                         );
674                         // textTpl
675                         $body .= TEMPLATE :: fill($this->getMemberOption($this->memid, 'textTpl'), $vars);
676                 } else {
677                         // 添付ファイルがある場合のbody
678                         $lastFile = array_pop($fileNames);
679                         
680                         foreach( $fileNames as $filename ){
681                                 $body .= $this->_imageHtml($filename);
682                         }
683                         
684                         $body .= $this->_imageHtml($lastFile, $text);
685                 }
686                                 
687                 // item追加
688                 $this->_info(__LINE__ . ": アイテム追加します");
689                 
690                 // bodyをbodyとmoreに分割
691                 $more = '';
692                 if( $this->moreDelimiter )
693                         list($body, $more) = spliti($this->moreDelimiter, $body, 2);
694                         
695                 $body = trim($body);
696                 $more = trim($more);
697                         
698                 $this->_addDatedItem($this->blogid, $subject, $body, $more, 0, $timestamp, 0, $this->categoryNameOrId);
699                 return true;
700         }
701         
702         function _decodeMultiPart($parts, &$texts, &$fileNames){
703                 foreach($parts as $part){\r                      switch ( strtolower( $part->ctype_primary )){
704                                 // multipart
705                                 case 'multipart':
706                                         $this->_decodeMultiPart($part->parts, $texts, $fileNames);
707                                         break;
708                                 //text part
709                                 case 'text':
710                                         $this->_info(__LINE__ . ": text part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
711                                         $texts[$part->ctype_secondary] = $this->_textPart($part);
712                                         break;
713                                 // imagepart
714                                 case 'image':
715                                 default:
716                                         $this->_info(__LINE__ . ": image/data part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
717                                         if( $fileName = $this->_imagePart($part) )
718                                                 $fileNames[] = $fileName;
719                                         break;
720                         }
721                 }
722         }
723
724         function _textPart(&$part){
725                 $encoding = false;
726                 if( $part->ctype_parameters )
727                         $encoding = $this->ctype_parameters['charset'];
728                 
729                 $text = $this->_convert($part->body, $encoding);
730                 $text = strip_tags($text, $this->no_strip_tags);
731                 
732                 $blog =& new BLOG($this->blogid);
733                 //blog設定で改行を<br />に置換onの場合
734                 if ($blog->getSetting('bconvertbreaks')) { 
735                         if ( strtolower($part->ctype_secondary) == 'html' ) {
736                                 //改行文字を削除、<br>タグを\nへ
737                                 $text = str_replace("\r\n", "\r", $text);
738                                 $text = str_replace("\r", "\n", $text);
739                                 $text = str_replace("\n", "", $text);
740                                 $text = str_replace("<br>", "\n", $text);
741                         }
742                 }
743                 return $text;
744         }
745         
746         function _imagePart(&$part){
747                 if( !$this->prefixDate ){
748                         $this->prefixDate  = date('YmdHis');
749                         $this->fileCount = 0;
750                 } else {
751                         $this->fileCount += 1;
752                 }
753                 $this->filePrefix = $this->prefixDate . sprintf('%02d', $this->fileCount);
754                 
755                 $filename = "";
756                 if( $part->d_parameters ){
757                         $filename = $part->d_parameters['filename'];
758                 } elseif( $part->ctype_parameters ){
759                         $filename = $part->ctype_parameters['name'];
760                 } else {
761                         $filename = $part->ctype_secondary;
762                 }
763
764                 $filename = $this->_convert($filename);
765                 $filename = $this->filePrefix . "-" . $filename;                
766                 $this->_info(__LINE__ . ": FileName($filename)");
767                 
768                 // subtypeチェック
769                 $size = strlen($part->body);
770                 if( eregi($this->subtype, trim($part->ctype_secondary) )){
771                         // サイズ、拡張子チェック
772                         if ($size < $this->maxbyte && !eregi($this->viri, $filename)) {
773                                                                 
774                                 $fp = fopen($this->tmpdir.$filename, "w");
775                                 fputs($fp, $part->body);
776                                 fclose($fp);
777                                         
778                                 $size = @getimagesize($this->tmpdir.$filename);
779                                 //サムネイル作成する場合
780                                 if ($this->thumb_ok && function_exists('ImageCreate')) {
781                                         //サムネイル作成する拡張子の場合
782                                         if ( preg_match("/$this->thumb_ext/i", $filename) ) {
783                                                 if ($size[0] > $this->W || $size[1] > $this->H) {
784                                                         $this->_thumb_create($this->tmpdir.$filename, $this->W, $this->H, $this->thumb_dir);
785                                                 }
786                                         }
787                                 }
788                                 return $filename;
789                         }
790                         $this->_warn(__LINE__ . ": 添付ファイルを無視します。(サイズ超過: $size B or 保存しないファイルに該当しています) [$part->ctype_primary/$part->ctype_secondary]");
791                         return false;
792                 }
793                 $this->_warn(__LINE__ . ": 添付ファイルを無視します。(subtypeチェック: $part->ctype_secondary が対応MIMEタイプに入っていますか?) [$part->ctype_primary/$part->ctype_secondary]");
794                 return false;   
795         }
796         
797         function _imageHtml($filename, $body = ""){
798                 global $CONF;
799                 
800                 $size = @getimagesize($this->tmpdir.$filename);
801                 $thumb_size = @getimagesize($this->thumb_dir.$this->_getThumbFileName($filename));
802                 $smallH = round($this->smallW / $size[0] * $size[1], 0);
803
804                 $vars = array (
805                         'thumbW' => $thumb_size[0],
806                         'thumbH' => $thumb_size[1],
807                         'reductionW' => $this->smallW, 
808                         'reductionH' => $smallH,
809                         'sizeW' => $size[0],
810                         'sizeH' => $size[1],
811                         'body' => $body,
812                         'thumbUrl' => $this->thumb_collection.'/'.$this->_getThumbFileName($filename),
813                         'imageUrl' => $this->collection.'/'.$filename,
814                         'mediaUrl' => $CONF['MediaURL'],
815                         'fileName' => $filename
816                 );
817                 
818                 // データファイルチェック
819                 if( ! preg_match("/$this->imgExt/i", $filename) ){
820                         $this->_info(__LINE__ . ": 画像ファイルに該当しないので、データファイルテンプレートを使用します");
821                         return TEMPLATE :: fill($this->getMemberOption($this->memid, 'dataTpl'), $vars);
822                 }
823
824                 if ( $thumb_size[0] ) { //サムネイルがある場合のソース
825                         $this->_info(__LINE__ . ": サムネイルがあります");
826                         return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withThumbTpl'), $vars);
827                 } else { //サムネイルがない場合のソース
828                         if ($size[0] > $this->smallW) { //縮小表示
829                                 $this->_info(__LINE__ . ": サムネイルがありません、縮小表示します");
830                                 return TEMPLATE :: fill($this->getMemberOption($this->memid, 'reductionTpl'), $vars);
831                         } else { //そのまま表示
832                                 $this->_info(__LINE__ . ": サムネイルがありません");
833                                 return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withoutThumbTpl'), $vars);
834                         }
835                 }
836         }
837         
838         function _addDatedItem($blogid, $title, $body, $more, $closed, $timestamp, $future, $catNameOrId = "") {
839                 // 1. ログイン======================
840                 $mem = MEMBER :: createFromID($this->memid);
841
842                 // 2. ブログ追加できるかチェック======================
843                 if (!BLOG :: existsID($this->blogid)) {
844                         $this->_info(__LINE__ . ": 存在しないblogです");
845                         return false;
846                 }
847                 $this->_info(__LINE__ . ": blogidはOK!");
848
849                 if (!$mem->isTeamMember($blogid)) {
850                         $this->_warn(__LINE__ . ": メンバーではありません");
851                         return false;
852                 }
853                 $this->_info(__LINE__ . ": メンバーチェックもok!");
854                 
855                 if (!trim($body)) {
856                         $this->_warn(__LINE__ . ": 空のアイテムは追加できません");
857                         return false;
858                 }
859                 $this->_info(__LINE__ . ": アイテムは空じゃないです");
860
861                 // 3. 値の補完
862                 $blog =& new BLOG($this->blogid);
863                 if( $blog->isValidCategory($catNameOrId) ){
864                         // カテゴリIDとして有効なときはそのまま使う
865                         $catid = $catNameOrId;
866                 } else {
867                         // カテゴリID ゲット (誤ったカテゴリID使用時はデフォを使用)
868                         $catid = $blog->getCategoryIdFromName($catNameOrId);
869                 }
870                 
871                 $this->_info(__LINE__ . ": 追加するcatid: ".$catid);
872                 if ($this->DefaultPublish) {
873                         $draft = 0;
874                 } else {
875                         $draft = 1; //ドラフト追加
876                         $this->_info(__LINE__ . ": ドラフトで追加します");
877                 }
878                 if ($closed != 1)
879                         $closed = 0; //コメントを許可
880                 $this->_info(__LINE__ . ": \$catid:".$catid.", \$draft:".$draft.", \$closed:".$closed);
881
882                 // 4. blogに追加
883                 $itemid = $blog->additem($catid, $title, $body, $more, $blogid, $mem->getID(), $timestamp, $closed, $draft);
884                 
885                 $this->_info(__LINE__ . ": itemid: $itemid");
886                 return $itemid;
887         }
888         
889         function _isSpam($str){
890                 global $manager;
891                 if( $this->getOption('spamCheck') == 'yes' ){   
892                         $spamcheck = array (
893                                 'type'          => 'Moblog',
894                                 'data'          => $str,
895                                 'return'        => true,
896                                 'ipblock'   => false
897                         );
898                         $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));
899                         if (isset($spamcheck['result']) && $spamcheck['result'] == true)
900                                 return true;
901                 }
902                 return false;
903         }
904 }