***添付ファイルの扱い -画像等の添付ファイルを指定することができます。 -添付ファイルは、PukiWikiに取り込む際にページの添付ファイルになります。 -添付ファイルは、本文の最後に &ref() で指定されます。 -同じファイル名の添付ファイルは上書き登録されます。 ***メールの取り込み +プラグインが記述された管理用のページを開きます。 +パスワードを入力し「メール取得」ボタンを押します。 +メールで送信された内容が編集ボックスに格納されます。~ ページ名は「引数に指定されたページ名/表題(Subject)」になります。 +ページ名が同じの場合、既存のページの後に追加されます。 +あとは通常の「編集」と同じです。 **管理用ページの設置方法 管理用のページにプラグインを指定します。 #contribute([ページ名]) -ページ名~ ページを追加するトップページ名を指定します。 省略した場合はルートに追加します。 **メールの自動取り込みの設定方法 次の設定をすると、メールを自動的にPukiWikiページに取り込むようになります。 ただし、管理者パスワードの確認を行わないので、十分注意して設定してください。 -contribute.inc.phpを開き、チェック間隔(''CONTRIBUTE_AUTO_INTERVAL'')を 設定します。 単位は'分'です。できるだけ大きな値を設定することをお勧めします。 -投稿を許可するメールアドレス(''CONTRIBUTE_AUTO_ENABLE_PATTERN'')を 設定します。 初期状態では全てのメールアドレスからの投稿を受け付けますのでご注意ください。 -スキン(skin/pukiwiki.skin.ja.php)の一番最後に次のコードを追記してください。 -投稿禁止リスト($contribute_ignore_list)は必要に応じて設定してください。 自動取り込みでメールを受信すると、ログファイルに記録が残ります。 ファイル名は 'CONTRIBUTE_AUTO_LOGFILE' で定義されています。 これも必要に応じて変更してください。 **注意 -メールに添付されたファイルは自動的にそのページの添付ファイルとなりますが、 ページを削除しても(登録しなくても)添付ファイルの削除は行いません。 -mailkeeperプラグインとは同じページに記述できません。 -最大受信サイズ(デフォルトでは100kbyte)を超えた場合、内容は保証されません ((それ未満でも保証しないわけですが・・・))。 -------------------------------*/ /* ja.lngに下記の定義を追加すること。 /////////////////////////////////////// // contribute.inc.php $contribute_msg = 'パスワードを入力してください。'; $contribute_invalid = 'パスワードが間違っています。'; $contribute_button = 'メール取得'; $contribute_pop_err = 'サーバーに接続できません。'; $contribute_none = '新しいメールはありません。'; */ ///////////////////////////////////////////////// // 下記の値を書き換えて使用すること! ///////////////////////////////////////////////// // メールサーバアドレス define('CONTRIBUTE_MAILSERVER','mail.server.address'); // User define('CONTRIBUTE_MAILUSER','username'); // Pass define('CONTRIBUTE_MAILPASS','password'); global $contribute_ignore_list; ///////////////////////////////////////////////// // 自動投稿チェックの間隔(0で投稿チェックしない) define('CONTRIBUTE_AUTO_INTERVAL','0'); // 投稿を許可するメールアドレスのパターン // ( [注意] このパターンに''合わない''ものは全て破棄されます ) define('CONTRIBUTE_AUTO_ENABLE_PATTERN','[\w\.\-]+@[\w\.\-]+'); // 投稿を許可しないメールアドレスの一覧(正規表現) // ( [注意] このパターンに''合う''ものは全て破棄されます ) $contribute_ignore_list = array ( 'attacker@hogehoge\.jp', '.+@attacker\.hogehoge\.jp', ); // 同一ページ名が存在したときに上書きをするか define('CONTRIBUTE_PAGE_OVERWRITE',FALSE); ///////////////////////////////////////////////// // ログファイル関連 // 自動投稿記録のファイル名 define('CONTRIBUTE_AUTO_LOGFILE', CACHE_DIR.'auto_contribute.log' ); // 自動投稿記録の最大件数 define('CONTRIBUTE_AUTO_LOGAGE','100'); // 自動投稿記録のフォーマット define('CONTRIBUTE_AUTO_LOGFORMAT',"\x08_NOW_\x08 <\x08_ADR_\x08> \x08_PAGE_\x08\n"); ///////////////////////////////////////////////// // 動作ステート define('CONTRIBUTE_STATE_NORMAL' , 0); define('CONTRIBUTE_STATE_AUTOGET', 1); ///////////////////////////////////////////////// // POPメール受信クラスの定義値 // POP3ポート define('CONTRIBUTE_MAILPORT','110'); // タイムアウト define('CONTRIBUTE_MAILTMOUT','30'); // 受信最大サイズ(default=102400 100kbyte) define('CONTRIBUTE_MAXSIZE','102400'); // 受信最大サイズを超えたときの受信行数 define('CONTRIBUTE_RCVLINE','500'); ///////////////////////////////////////////////// // 受信したメールを消去する 1:消去 0:サーバに残す define('CONTRIBUTE_MAIL_DELETE','0'); // APOPによる認証 0:使用しない 1:使用する define('CONTRIBUTE_USE_APOP','0'); ///////////////////////////////////////////////// // last UID file define('CONTRIBUTE_FILE_UID',CACHE_DIR.'contribute_uid.dat'); ///////////////////////////////////////////////// // POPメール解析クラス class popmail_parse { var $plain, $header, $body; var $content_type, $content_subtype , $content_param , $content_encoding; var $content_disposition, $content_sub_disposition; var $to, $from, $msgid, $datetime, $subject; function popmail_parse($plain) { unset($this->plain); unset($this->header); unset($this->body); unset($this->content_type); unset($this->content_subtype); unset($this->content_param); unset($this->to); unset($this->from); unset($this->msgid); unset($this->datetime); unset($this->subject); $this->plain = $plain; } function analyze($plain) { // // Header 及び body の分離 // if($plain!='') { $this->plain = $plain; } $ex = explode( "\r\n\r\n" , $this->plain , 2 ); $header_temp = $ex[0]; $body_temp = $ex[1]; // // マルチラインヘッダーの正規化 // $header_array = explode( "\n" , $header_temp ); $this->header = ''; foreach( $header_array as $hl ) { $matches = array(); if( preg_match("/^((?:\s|\t)+)(.*)/",$hl,$matches )==True ) { $this->header .= trim($matches[2]); } else { $this->header .= "\n".trim($hl); } } $this->header .= "\n"; $this->body = $body_temp; // // Content-Type: のチェック // if( preg_match("/[\^\n]content-type: (\S+)\/(\S+)\s*;\s*(.*)/i",$this->header,$match)==True ) { $this->content_type = $match[1]; $this->content_subtype = $match[2]; $this->content_param = $match[3]; } // // Content-Transfer-Encoding: のチェック // if( preg_match("/[\^\n]content-transfer-encoding: (\S+)/i",$this->header,$match)==True ) { $this->content_encoding = $match[1]; //echo $this->content_encoding."/ "; } // Content-Disposition: のチェック if( preg_match("/[\^\n]content-disposition: (\w+)\s*;\s*(.+)/i",$this->header,$match)==True ) { $this->content_disposition = $match[1]; $this->content_sub_disposition = $match[2]; } // // To: のチェック // if( preg_match("/[\^\n]to:\s+<([\w\.\-]+@[\w\.\-]+)>/i",$this->header,$match)==True ) { $this->to = $match[1]; } else if( preg_match("/[\^\n]to:\s+([\w\.\-]+@[\w\.\-]+)/i",$this->header,$match)==True ) { $this->to = $match[1]; } // // From: のチェック // if( preg_match("/[\^\n]from:\s+.*<([\w\.\-]+@[\w\.\-]+)>/i",$this->header,$match)==True ) { $this->from = $match[1]; } else if( preg_match("/[\^\n]from:\s+([\w\.\-]+@[\w\.\-]+)/i",$this->header,$match)==True ) { $this->from = $match[1]; } // // Message-Id: のチェック // if( preg_match("/[\^\n]message-id:/i",$this->header,$match)==True ) { $this->msgid = $match[1]; } // 日付の抽出 $this->datetime = -1; if( preg_match("/[\^\n]date:[ \t]*([^\r\n]+)/i",$this->header,$match)==True ) { $this->datetime = strtotime($match[1]); } if ($this->datetime == -1) { $this->datetime = time(); } // サブジェクトの抽出 if (eregi("\nSubject:[ \t]*([^\r\n]+)", $this->header, $subreg)) { // =?iso-2022-jp〜 をデコードする $this->subject = $this->text_decode($subreg[1]); } // // Multipartチェック(RFC2046) // (再帰呼び出し) // $content = strtolower($this->content_type) . "/" . strtolower($this->content_subtype); //echo $content . "/ \n"; if( $content=="multipart/mixed" || $content=="multipart/alternative" || $content=="multipart/parallel" || $content=="multipart/digest" ) { if( preg_match('/boundary="([^"]+)"/i' , $this->content_param , $match ) ) { $boundary = '--'.$match[1]; } else { return FALSE; } // echo "(!) Boundary => (" . $boundary . ")\n"; $bounding_array = explode( $boundary , $this->body ); $result = array(); foreach( $bounding_array as $id => $bounding_body ) { if( $id==0 ) continue; if( preg_match('/^--/',$bounding_body) ) { break; } $bbody = new popmail_parse($bounding_body); $r = $bbody->analyze(); if($r!=FALSE) { foreach( $r as $key => $data ) { $result[] = $data; } } } return $result; } // // BASE64 / uuencode // if( $this->content_encoding=="base64" ) { $data = base64_decode($this->body); } else if( $this->content_encoding=="quoted-printable" ) { $data = quoted_printable_decode($this->body); } else { $data = $this->body; } if($content=="text/plain") { if(function_exists('mb_convert_encoding')) { // テキストを変換する $data = mb_convert_encoding($data, SOURCE_ENCODING, "auto"); } } $filename = ''; if( preg_match("/name\=\"(.+)\"/i",$this->content_param,$match)==True ) { // Content-Type: にname指定あり $filename = $match[1]; } if( preg_match("/filename\=\"(.+)\"/i",$this->content_sub_disposition,$match)==True ) { // こちらがContent-Typeの指定より優先される // Content-Disposition: にfilename指定あり $filename = $match[1]; } // ファイル名を変換する $filename = $this->text_decode($filename); $result[] = array("Content-Type" => $content , "Body" => $data , "Filename" => $filename); return $result; } function to() { return $this->to; } function from() { return $this->from; } function msgid() { return $this->msgid; } function datetime() { return strftime("%Y/%m/%d %H:%M:%S",$this->datetime); } function date() { return strftime("%Y-%m-%d",$this->datetime); } function body() { return $this->body; } function header() { return $this->header; } function subject() { return $this->subject; } function content_type() { return $this->content_type."/".$this->content_subtype; } function text_decode($str) { //MIME Bデコ−ド while (eregi("(.*)=\?iso-2022-jp\?B\?([^\?]+)\?=(.*)",$str,$regs)) { $str = $regs[1].base64_decode($regs[2]).$regs[3]; } //MIME Qデコ−ド while (eregi("(.*)=\?iso-2022-jp\?Q\?([^\?]+)\?=(.*)",$str,$regs)) { $str = $regs[1].quoted_printable_decode($regs[2]).$regs[3]; } if(function_exists('mb_convert_encoding')) { $str = mb_convert_encoding($str, SOURCE_ENCODING, "auto"); } return htmlspecialchars($str); } } class pop3_protocol { var $sock; var $mailnum, $totalsize; var $uid, $break_flg; function pop3_protocol() { unset($this->sock); unset($this->mailnum); unset($this->totalsize); } ////////////////////////////////////////////////////////// // POP3接続を開始して受信メール件数とサイズを得る function connect($server,$pop_port,$timeout,$user,$pass,$apop) { // POP3接続開始 $this->sock = fsockopen($server, $pop_port, $err, $errno, $timeout); if($this->sock!=FALSE) { $buf = fgets($this->sock, 512); if(substr($buf, 0, 3) != '+OK') { die($buf); // サーバ接続エラー } if($apop==0) { // クリアテキスト認証 $buf = $this->sendcmd($this->sock,"USER $user"); $buf = $this->sendcmd($this->sock,"PASS $pass"); } else { // APOPを使用する if( preg_match("/^\+OK\s+.*(<+.*>)/",$buf,$match)==True ) { $apop = md5($match[1].$pass); $buf = $this->sendcmd($this->sock,"APOP $user $apop"); } } // 受信済みメールの件数とサイズを得る $buf = $this->sendcmd($this->sock,"STAT"); sscanf($buf, '+OK %d %d',$this->mailnum,$this->totalsize); } return $this->sock; } ////////////////////////////////////////////////////////// // POP3接続を終了する function disconnect() { $buf = $this->sendcmd($this->sock,"QUIT"); fclose($this->sock); } ////////////////////////////////////////////////////////// // 指定番号のメールを受信する function get_mail($num,$maxsize,$rcvline) { //UIDL n -n番目のUID取得 $buf = $this->sendcmd($this->sock,"UIDL $num"); sscanf($buf, '+OK %d %s',$dummy,$this->uid); //LIST n -n番目のサイズ取得(ヘッダ含) $buf = $this->sendcmd($this->sock,"LIST $num"); sscanf($buf, '+OK %d %ld',$dummy,$size); if($maxsize>0 && $size>$maxsize) { // 受信サイズ制限にかかった $buf = $this->sendcmd($this->sock,"TOP $num ".$rcvline); } else { //RETR n -n番目のメッセージ取得(ヘッダ含) $buf = $this->sendcmd($this->sock,"RETR $num"); } while (!ereg("^\.\r\n",$buf)) { //EOFの.まで読む $buf = fgets($this->sock,512); $data.= $buf; } // 取得したメールデータの検証 $mail_len = strlen($data) - 3; // LISTで取得したサイズと実際の受信データのサイズを比較 $this->break_flg = 0; if($mail_len != $size) { // TOPコマンド受信で全て受信できていない $this->break_flg = 1; } return $data; } ////////////////////////////////////////////////////////// // 指定番号のメールを消去する function delete_mail($num) { //DELE n n番目のメッセージ削除 $buf = $this->sendcmd($this->sock,"DELE $num"); } ////////////////////////////////////////////////////////// // 最後に取得したメールのUIDを得る function get_uid() { return $this->uid; } ////////////////////////////////////////////////////////// // 受信件数を返す function get_mailnum() { return $this->mailnum; } ////////////////////////////////////////////////////////// // 全受信メールのサイズを返す function get_totalsize() { return $this->totalsize; } ////////////////////////////////////////////////////////// // 新着メールの確認(0..新着メールなし) function check_newmail($last_uid) { $num = $this->mailnum; // 受信しているメール数 $newnum = 1; while($num>0) { //UIDL n -n番目のUID取得 $buf = $this->sendcmd($this->sock,"UIDL $num"); sscanf($buf, '+OK %d %s',$dummy,$check); if(strcmp($check,$last_uid)==0) { // 最後に受信したメールを見つけた if($num==$this->mailnum) { // 新着メールなし $newnum = 0; } else { $newnum = $num + 1; } break; } $num--; } // 一致しなければ、全てが新着メール return $newnum; } ////////////////////////////////////////////////////////// // 最後に受信したメールのUIDを得る function get_lastuid($file) { if(file_exists($file)) { $fp = fopen($file, 'r') or die('cannot open '.$file); $last_uid = rtrim(fgets($fp,256)); // ファイルを閉じる fclose($fp); } else { $last_uid = ''; } return $last_uid; } ////////////////////////////////////////////////////////// // 最後に受信したメールのUIDを保存する function set_lastuid($file) { $fp = fopen($file, 'w') or die('cannot write '.$file); set_file_buffer($fp, 0); flock($fp,LOCK_EX); rewind($fp); ftruncate($fp,0); fputs($fp,$this->uid); flock($fp,LOCK_UN); fclose($fp); } ////////////////////////////////////////////////////////// // 最後に受信したメールが破損しているかどうか function is_broken() { return $this->break_flg; } // POP3コマンド送信 function sendcmd($sock,$cmd) { fputs($sock, $cmd."\r\n"); $buf = fgets($sock, 512); if(substr($buf, 0, 3) == '+OK') { return $buf; } else { die($cmd." >> ".$buf); } } } // //------------------------------------------------------------------------ // プラグインの本体はここから //------------------------------------------------------------------------ // function plugin_contribute_action() { global $script,$vars,$post,$adminpass; global $contribute_invalid; $pass = array_key_exists('pass' ,$post) ? $post['pass'] : NULL; $toppage = array_key_exists('toppage',$post) ? $post['toppage'] : NULL; $post['page'] = $vars['page'] = $vars['refer']; // パスワード確認 if(md5($pass) == $adminpass) { // メールをチェックして取り込む $retval = plugin_contribute_getpage(CONTRIBUTE_STATE_NORMAL,$toppage); $title = $retval['title']; $body = $retval['body']; } else { $title = ($pass === NULL) ? '' : "
$contribute_invalid
\n"; $body = "$title
"; } $retvars['msg'] = $title; $retvars['body'] = $body; return $retvars; } function plugin_contribute_convert() { global $script,$vars; global $contribute_msg,$contribute_button; $toppage = ''; if(func_num_args()) { $args = func_get_args(); $toppage = array_shift($args); } $page = htmlspecialchars($vars['page']); $string = <<$title
"; } // POP接続の切断 $pop->disconnect(); return array('title'=>$title, 'body'=>$body); } // メール投稿チェック function plugin_contribute_check() { // 自動更新が有効な場合、一定間隔でメールの投稿をチェックする if(CONTRIBUTE_AUTO_INTERVAL > 0) { $file = CONTRIBUTE_AUTO_LOGFILE; if(file_exists($file)==TRUE) { // ログファイルの更新時刻が最後に取った時刻とする $progress = time() - filemtime(CONTRIBUTE_AUTO_LOGFILE); if($progress > (CONTRIBUTE_AUTO_INTERVAL * 60)) { // ファイルの最終更新日をセット touch($file); // あなたはジョーカーを引いてしまいました・・・ // これから投稿チェックを行います。 plugin_contribute_getpage(CONTRIBUTE_STATE_AUTOGET,''); } } else { // 初回起動 $fp = fopen($file, 'wb'); if($fp==FALSE) { die_message('contribute.inc.php:cannot create '.$file); } fclose($fp); } } } ?>