OSDN Git Service

NP_Moblog v1.17
authorhsur <hsur@1ca29b6e-896d-4ea0-84a5-967f57386b96>
Sun, 6 Jun 2010 12:05:39 +0000 (12:05 +0000)
committerhsur <hsur@1ca29b6e-896d-4ea0-84a5-967f57386b96>
Sun, 6 Jun 2010 12:05:39 +0000 (12:05 +0000)
git-svn-id: https://svn.sourceforge.jp/svnroot/nucleus-jp/plugin@1063 1ca29b6e-896d-4ea0-84a5-967f57386b96

12 files changed:
trunk/NP_Moblog/NP_Moblog.php
trunk/NP_Moblog/moblog/help.html
trunk/NP_Moblog/moblog/index.php
trunk/NP_Moblog/sharedlibs/Mail/RFC822.php
trunk/NP_Moblog/sharedlibs/Mail/mimeDecode.php
trunk/NP_Moblog/sharedlibs/Net/POP3.php
trunk/NP_Moblog/sharedlibs/Net/Socket.php
trunk/NP_Moblog/sharedlibs/Net/URL.php
trunk/NP_Moblog/sharedlibs/PEAR.php
trunk/NP_Moblog/sharedlibs/cles/Feedback.php
trunk/NP_Moblog/sharedlibs/cles/Template.php
trunk/NP_Moblog/sharedlibs/sharedlibs.php

index 34b7ec1..a98c33a 100644 (file)
-<?php
-// vim: tabstop=2:shiftwidth=2
-
-/**
-  * NP_Moblog ($Revision: 1.1 $)
-  * by hsur ( http://blog.cles.jp/np_cles )
-  * $Id: NP_Moblog.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-  *
-  * Based on NP_HeelloWorld v0.8 
-  * http://nakahara21.com/?itemid=133
-*/
-
-/*
-  * Copyright (C) 2003 nakahara21 All rights reserved.
-  * Copyright (C) 2004-2007 cles All rights reserved.
-  *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * as published by the Free Software Foundation; either version 2
-  * of the License, or (at your option) any later version.
-  * 
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  * 
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-  * 
-  * In addition, as a special exception, mamio and cles gives
-  * permission to link the code of this program with those files in the PEAR
-  * library that are licensed under the PHP License (or with modified versions
-  * of those files that use the same license as those files), and distribute
-  * linked combinations including the two. You must obey the GNU General Public
-  * License in all respects for all of the code used other than those files in
-  * the PEAR library that are licensed under the PHP License. If you modify
-  * this file, you may extend this exception to your version of the file,
-  * but you are not obligated to do so. If you do not wish to do so, delete
-  * this exception statement from your version.
-*/
-
-global $DIR_LIBS;
-require_once($DIR_LIBS . 'MEDIA.php');
-
-// クラスのロード
-require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
-require_once('Net/POP3.php');
-require_once('Mail/mimeDecode.php');
-require_once('Mail/RFC822.php');
-
-// バージョンチェック
-$required = '4.3.0';
-if( ! version_compare(phpversion() , $required , '>=') ){
-       ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
-}
-
-if (!function_exists('sql_table')) {
-       function sql_table($name) {
-               return 'nucleus_'.$name;
-       }
-}
-
-class NP_Moblog extends NucleusPlugin {
-
-       // name of plugin
-       function getName() {
-               return 'Moblog';
-       }
-
-       // author of plugin
-       function getAuthor() {
-               return 'hsur';
-       }
-
-       // an URL to the plugin website
-       // can also be of the form mailto:foo@bar.com
-       function getURL() {
-               return 'http://blog.cles.jp/np_cles/category/31/subcatid/2';
-       }
-
-       // version of the plugin
-       function getVersion() {
-               return '1.16';
-       }
-       
-       function hasAdminArea() {
-               return 1;
-       }
-       
-       function getEventList() {
-               return array('PrePluginOptionsEdit');
-       }
-       
-       function event_PrePluginOptionsEdit(&$data) {
-               switch($data['context']){
-                       case 'member':
-                               // idandcat
-                               $m =& MEMBER :: createFromID($data['contextid']);
-                               $trimChar = array(
-                                       '=' => '',
-                                       '|' => '',
-                                       ';' => '',
-                               );
-                               
-                               $blogs = Array();
-                               $res = mysql_query('SELECT bnumber, bname FROM '.sql_table('blog'));
-                               while( $o = mysql_fetch_object($res) ){
-                                        if( $m->isTeamMember($o->bnumber) ){
-                                               $blogs[$o->bnumber] = $o->bname;
-                                        }
-                               }
-                               
-                               $idandcatTypeInfo = '';
-                               foreach($blogs as $blogid => $blogname){
-                                       $res = mysql_query('SELECT catid, cname FROM '.sql_table('category').' WHERE cblog='.$blogid);
-                                       if( @mysql_num_rows($res) > 0) {
-                                               while( $o = mysql_fetch_object($res) ){
-                                                       if($idandcatTypeInfo)
-                                                               $idandcatTypeInfo .= '|';
-                                                       $o->cname = strtr($o->cname, $trimChar);
-                                                       $blogname = strtr($blogname, $trimChar);
-                                                       $idandcatTypeInfo .= "{$o->cname} ({$blogname})|{$blogid},{$o->catid}";
-                                               }
-                                       }
-                               }
-                               if( ! $idandcatTypeInfo ){
-                                       $idandcatTypeInfo = "!!投稿可能なblogがありません!!|0,0";
-                               }
-                               
-                               // collection & thumb_col
-                               $collections = MEDIA::getCollectionList();
-                               $mid = intval($m->getID());
-                               $collections[$mid] = 'デフォルト(useridディレクトリ)';
-                               
-                               $collectionTypeInfo = '';
-                               foreach( $collections as $collection => $name ){
-                                       if($collectionTypeInfo) $collectionTypeInfo .= '|';
-                                       $name = strtr($name, $trimChar);
-                                       $collection = strtr($collection, $trimChar);
-                                       $collectionTypeInfo .= "{$name}|{$collection}";
-                               }
-                               
-                               // set options
-                               foreach($data['options'] as $oid => $option ){
-                                       switch($data['options'][$oid]['name']){
-                                               case 'idandcat':
-                                                       $data['options'][$oid]['typeinfo'] = $idandcatTypeInfo;
-                                                       break;
-                                               case 'collection':
-                                               case 'thumb_col':
-                                                       $data['options'][$oid]['typeinfo'] = $collectionTypeInfo;
-                                                       break;
-                                       }
-                               }
-                                                               
-                               break;
-                       default:
-                               // nothing      
-               }
-       }
-       
-       function install() {
-               $this->createMemberOption('enable', 'プラグインを有効にするか?', 'yesno', 'no');
-
-               $this->createMemberOption('host', 'POP3 ホスト名', 'text', 'localhost');
-               $this->createMemberOption('port', 'POP3 ポート', 'text', '110', 'numerical=true');
-               $this->createMemberOption('user', 'POP3 ユーザー名', 'text', '');
-               $this->createMemberOption('pass', 'POP3 パスワード', 'password', '');
-               $this->createMemberOption('useAPOP', 'APOPを使用するか?', 'yesno', 'no');
-
-               $this->createMemberOption('idandcat', 'Nucleusカテゴリ(Blog)', 'select', '', '');
-
-               $this->createMemberOption('collection', '画像を保存するディレクトリ', 'select', '', '');
-               $this->createMemberOption('thumb_col', 'サムネイルをを保存するディレクトリ', 'select', '', '');
-
-               $this->createMemberOption('imgonly', 'イメージ添付メールのみ追加?', 'yesno', 'no');
-               $this->createMemberOption('DefaultPublish', 'デフォルトで公開するか?', 'yesno', 'no');
-
-               $this->createMemberOption('optionsKeyword', 'オプション記述開始の区切り文字', 'text', '@');
-               $this->createMemberOption('blogKeyword', 'オプションでblogidを指定する場合のキー', 'text', 'b');
-               $this->createMemberOption('categoryKeyword', 'オプションでカテゴリを指定する場合のキー', 'text', 'c');
-               $this->createMemberOption('publishKeyword', 'オプションでストレートにpublish指定する場合のキー', 'text', 's');
-
-               $this->createMemberOption('moreDelimiter', '追記にする場合の区切り文字(利用しない場合は空欄)', 'text', '');
-
-               $this->createMemberOption('accept', '投稿許可アドレス(複数の場合改行で区切ってください)', 'textarea', '');
-               $this->createMemberOption('acceptSubjectPrefix','投稿許可SubjectPrefix(制限無しの場合は空欄)','text','');
-
-               $this->createMemberOption('nosubject', '件名がないときの題名', 'text', '');
-               $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>');
-               $this->createMemberOption('maxbyte', '最大添付量(B)', 'text', '300000', 'numerical=true');
-               $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');
-               $this->createMemberOption('viri', '保存しないファイル(正規表現)', 'text', '.+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$');
-               $this->createMemberOption('imgExt', '画像ファイルの拡張子(正規表現)', 'text', '.+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$');
-
-               $this->createMemberOption('thumb_ok', 'サムネイルを使用する?', 'yesno', 'yes');
-               $this->createMemberOption('W', 'サムネイルの大きさ(Width)', 'text', '120', 'numerical=true');
-               $this->createMemberOption('H', 'サムネイルの大きさ(Hight)', 'text', '120', 'numerical=true');
-               $this->createMemberOption('thumb_ext', 'サムネイルを作る対象画像', 'text', '.+\.jpe?g$|.+\.png$');
-               $this->createMemberOption('smallW', 'アイテム内に表示する画像の最大横幅', 'text', '120', 'numerical=true');
-
-               $this->createMemberOption('textTpl', 'テキストテンプレート', 'textarea', '<%body%>');
-               $this->createMemberOption('withThumbTpl', 'サムネイル付きテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%thumbUrl%>|<%thumbW%>|<%thumbH%>|)%></a></div><%body%>');
-               $this->createMemberOption('withoutThumbTpl', 'サムネイルなしテンプレート', 'textarea', '<div class="leftbox"><%image(<%imageUrl%>|<%sizeW%>|<%sizeH%>|)%></div><%body%>');
-               $this->createMemberOption('reductionTpl', 'サムネイルなしテンプレート(縮小)', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%imageUrl%>|<%reductionW%>|<%reductionH%>|)%></a></div><%body%>');
-               $this->createMemberOption('dataTpl', 'データファイルテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%fileName%></a></div><%body%>');
-               
-               $this->createOption('execMode', '動作モード', 'select', '1', '振分対応モード|0|互換モード|1');
-               $this->createOption('spamCheck', 'SPAMチェックを有効にする', 'yesno', 'no');
-               
-               $this->createOption('interval', 'メール取得の間隔(秒)', 'text', '600', 'numerical=true');
-               $this->createOption('nextUpdate', '次回更新時刻(変更できません)', 'text', '-', 'access=readonly');
-               $this->createOption('lastUpdate', '最終更新時刻(UnixTimeStamp,)', 'text', '0', 'access=hidden');
-               $this->createOption('debug', 'ログを出力を行うか?', 'yesno', 'no');
-       }
-       
-       function unInstall() {}
-       function getMinNucleusVersion() { return 320; }
-       function getMinNucleusPatchLevel() { return 0; }
-
-       // a description to be shown on the installed plugins listing
-       function getDescription() {
-               return '[$Revision: 1.1 $]<br />メールを拾ってアイテムを追加します。&lt;%Moblog%&gt;の記述のあるスキンを適用するページを開くと実行されます。<br />
-                               &lt;%Moblog(link)%&gt;と記入することでメールを取得するためのリンクを表示することができます(要ログイン)<br />
-                               個人ごとに設定ができるようになりましたので「あなたの設定」か「メンバー管理」から設定を行ってください。';
-       }
-
-       function supportsFeature($what) {
-               switch ($what) {
-                       case 'SqlTablePrefix' :
-                       case 'HelpPage':
-                               return 1;
-                       default :
-                               return 0;
-               }
-       }
-
-       function _info($msg) {
-               if ($this->getOption('debug') == 'yes') {
-                       ACTIONLOG :: add(INFO, 'Moblog: '.$msg);
-               }
-       }
-
-       function _warn($msg) {
-               ACTIONLOG :: add(WARNING, 'Moblog: '.$msg);
-       }
-
-       function _getEnableUserId() {
-               $userOptions = $this->getAllMemberOptions('enable');
-               $userIds = Array ();
-               foreach( $userOptions as $userId => $value ){
-                       if( $value == 'yes') $userIds[] = $userId;
-               }
-               return $userIds;
-       }
-
-       function _initMediaDirByUserId($userId) {
-               global $DIR_MEDIA;
-
-               $this->_info(__LINE__ . ": ユーザ($userId)の初期設定");
-               $this->memid = $userId;
-
-               /*-- 受信メールサーバーの設定--*/
-               $this->host = $this->getMemberOption($userId, 'host');
-               $this->port = $this->getMemberOption($userId, 'port');
-               $this->user = $this->getMemberOption($userId, 'user');
-               $this->pass = $this->getMemberOption($userId, 'pass');  
-
-               // メールでアイテムを追加するblogのID
-               $idandcat = $this->getMemberOption($userId, 'idandcat');
-               list($this->blogid, $this->categoryNameOrId) = explode(",", $idandcat);
-               
-               $this->imgonly = ($this->getMemberOption($userId, 'imgonly') == 'yes') ? 1 : 0;
-               $this->DefaultPublish = ($this->getMemberOption($userId, 'DefaultPublish') == 'yes') ? 1 : 0;
-
-               /*-- メールのタイトルに各種オプションを含める場合の設定--*/
-               $this->optionsKeyword = $this->getMemberOption($userId, 'optionsKeyword');
-               $this->blogKeyword = $this->getMemberOption($userId, 'blogKeyword');
-               $this->categoryKeyword = $this->getMemberOption($userId, 'categoryKeyword');
-               $this->publishKeyword = $this->getMemberOption($userId, 'publishKeyword');
-
-               // 投稿許可アドレス
-               $this->accept = explode("\n", $this->getMemberOption($userId, 'accept'));
-               $this->accept = Array_Map("Trim", $this->accept);
-               $this->accept = Array_Map("strtolower", $this->accept);
-               foreach( $this->accept as $mailAddr ){
-                       $this->_info(__LINE__ . "許可アドレス user:$userId, $mailAddr");
-               }               
-
-               // 投稿許可SubjectPrefix
-               $this->acceptSubjectPrefix = Trim($this->getMemberOption($userId, 'acceptSubjectPrefix'));
-               
-               // 追記の区切り
-               $this->moreDelimiter = Trim($this->getMemberOption($userId, 'moreDelimiter'));
-               
-               // 件名がないときの題名
-               $this->nosubject = $this->getMemberOption($userId, 'nosubject');
-               $this->no_strip_tags = $this->getMemberOption($userId, 'no_strip_tags');
-
-               // 最大添付量(バイト・1ファイルにつき)※超えるものは保存しない
-               $this->maxbyte = $this->getMemberOption($userId, 'maxbyte');
-               $this->subtype = $this->getMemberOption($userId, 'subtype');
-               $this->viri = $this->getMemberOption($userId, 'viri');
-               $this->imgExt = $this->getMemberOption($userId, 'imgExt');
-
-               // サムネイル
-               $this->thumb_ok = ($this->getMemberOption($userId, 'thumb_ok') == 'yes') ? 1 : 0;
-               $this->W = $this->getMemberOption($userId, 'W');
-               $this->H = $this->getMemberOption($userId, 'H');
-               $this->thumb_ext = $this->getMemberOption($userId, 'thumb_ext');
-               $this->smallW = $this->getMemberOption($userId, 'smallW');
-               
-               // 画像保存ディレクトリ
-               $collections = MEDIA::getCollectionList();
-               $collection = $this->getMemberOption($userId, 'collection');
-               if( $collection && isset($collections[$collection]) ){
-                       $this->collection = $collection;
-               } else {
-                       $this->_info(__LINE__ . ": 画像保存ディレクトリが正しくありません。デフォルトを使用します");
-                       $this->collection = $this->memid;
-               }
-               
-               $this->tmpdir = $DIR_MEDIA.$this->collection.'/';
-               $this->_info(__LINE__ . ": 画像保存ディレクトリ: $this->tmpdir");
-               
-               if (!@is_dir($this->tmpdir)) {
-                       $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないので、ディレクトリを作成します。");
-                       $oldumask = umask(0000);
-                       if (!@mkdir($this->tmpdir, 0777))
-                               return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
-                       umask($oldumask);                               
-               } 
-
-               if (!is_writable($this->tmpdir)) {
-                       $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないか、書き込み可能になっていません");
-               }
-               
-               // サムネイル保存ディレクトリ
-               $thumb_collection = $this->getMemberOption($userId, 'thumb_col');
-               if( $collection && isset($collections[$thumb_collection]) ){
-                       $this->thumb_collection = $thumb_collection;
-               } else {
-                       $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリが正しくありません。デフォルトを使用します");
-                       $this->thumb_collection = $this->memid;
-               }
-
-               $this->thumb_dir = $DIR_MEDIA.$this->thumb_collection.'/';
-               $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリ: $this->thumb_dir");
-               
-               if (!@is_dir($this->thumb_dir)) {
-               $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないので、ディレクトリを作成します。");
-                       $oldumask = umask(0000);
-                       if (!@mkdir($this->thumb_dir, 0777))
-                               return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
-                       umask($oldumask);                               
-               } 
-               
-               if (!is_writable($this->thumb_dir)) {
-                       $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないか、書き込み可能になっていません");
-               }
-       }
-
-       function _convert($str, $input_encoding = false) {
-               if( ! $input_encoding ){
-                       $input_encoding = "ISO-2022-JP,ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-2022-JP";
-                       $encoding = mb_detect_encoding($input_encoding);
-                       if( ! $encoding )
-                               $input_encoding = "ISO-2022-JP";
-               }
-               return mb_convert_encoding($str, _CHARSET, $input_encoding);
-       }
-       
-       function _addr_search($str) {
-               if( PEAR::isError($addresses = Mail_RFC822::parseAddressList($str)) ){
-                       return false;
-               }
-               $addr = array_shift($addresses);
-               if($addr)
-                       return $addr->mailbox . "@" .$addr->host;
-               return false;
-       }
-
-       function _thumb_create($src, $W, $H, $thumb_dir = "./") {
-                       // 画像の幅と高さとタイプを取得
-                               $size = GetImageSize($src);
-               switch ($size[2]) {
-                       case 1 :
-                               return false;
-                               break;
-                       case 2 :
-                               $im_in = @ImageCreateFromJPEG($src);
-                               break;
-                       case 3 :
-                               $im_in = @ImageCreateFromPNG($src);
-                               break;
-               }
-               if (!$im_in) {
-                       $this->_warn(__LINE__ . ": GDをサポートしていないか、ソースが見つかりません<br>phpinfo()でGDオプションを確認してください");
-                       return false;
-               }
-               // リサイズ
-               if ($size[0] > $W || $size[1] > $H) {
-                       $key_w = $W / $size[0];
-                       $key_h = $H / $size[1];
-                       ($key_w < $key_h) ? $keys = $key_w : $keys = $key_h;
-                       $out_w = $size[0] * $keys;
-                       $out_h = $size[1] * $keys;
-               } else {
-                       $out_w = $size[0];
-                       $out_h = $size[1];
-               }
-               // 出力画像(サムネイル)のイメージを作成し、元画像をコピーします。(GD2.0用)
-               $im_out = ImageCreateTrueColor($out_w, $out_h);
-               $resize = ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $size[0], $size[1]);
-
-               // サムネイル画像をブラウザに出力、保存
-               $filename = substr($src, strrpos($src, "/") + 1);
-               ImageJPEG($im_out, $thumb_dir.$this->_getThumbFileName($filename)); //jpgサムネイル作成
-               // 作成したイメージを破棄
-               ImageDestroy($im_in);
-               ImageDestroy($im_out);
-               
-               $this->_info(__LINE__ . ": サムネイルを作成しました" . $thumb_dir.$this->_getThumbFileName($filename));
-               return true;
-       }
-       
-       function _getThumbFileName($filename){
-               $filename = substr($filename, 0, strrpos($filename, "."));
-               return $filename."-small.jpg";
-       }
-
-       function _getExecuteLink() {
-               global $CONF;
-               return $CONF['ActionURL'].'?action=plugin&amp;name=Moblog&amp;type=execute';
-       }
-
-       function _checkLastupdate() {
-               $now = time();
-               $lastUpdate = $this->getOption('lastUpdate');
-               $interval = $this->getOption('interval');
-
-               if ($lastUpdate + $interval < $now) {
-                       $this->setOption('lastUpdate', $now);
-                       $this->setOption('nextUpdate', date("Y-m-d H:i:s", $lastUpdate + $interval));
-                       $this->_info(__LINE__ . ": 更新します。");
-                       return true;
-               }
-               $this->_info(__LINE__ . ": 更新しません。次回更新は".date("Y-m-d H:i:s", $lastUpdate + $interval)."以降です。");
-               return false;
-       }
-
-       function doSkinVar($skinType, $type = "") {
-               global $member;
-               switch ($type) {
-                       case '' :
-                       case 'execute' :
-                               if ( $this->_checkLastupdate() )
-                                       $this->execute();
-                               break;
-
-                       case 'link' :
-                               if ( $member->isLoggedIn() )
-                                       echo '<a href="'.$this->_getExecuteLink().'">Add Item by Mail</a>';
-                               break;
-               }
-       } //end of function doSkinVar($skinType)
-
-       function doAction($type) {
-               global $member;
-
-               switch ($type) {
-                       case '' :
-                       case 'execute' :
-                               if (!$member->isLoggedIn())
-                                       return "ログインが必要です";
-                               $this->execute();
-                               header('Location: ' . serverVar('HTTP_REFERER'));
-                               break;
-
-                       default :
-                               return 'アクションが定義されていません: '.$type;
-               }
-       }
-
-       function execute() {
-               $this->execMode = intval($this->getOption('execMode'));
-               if( $this->execMode == 0 ){
-                       // false
-                       $this->_info(__LINE__ . ": 振分対応モードで動作します。");
-               } elseif ( $this->execMode == 1 ) {
-                       // true
-                       $this->_info(__LINE__ . ": 互換モードで動作します");                 
-               }
-               
-               $enabledUserIds = $this->_getEnableUserId();
-               foreach ($enabledUserIds as $userId) {
-                       $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得開始します");
-                       $this->_initMediaDirByUserId($userId);
-                                               
-                       // 接続
-                       $pop3 =& new Net_POP3();
-                       $pop3->_timeout = 10;
-                       
-                       if( ! $pop3->connect($this->host, $this->port) ){
-                               $this->_warn(__LINE__ . ": POPサーバーに接続できません");
-                               continue;
-                       }
-                       
-                       // 認証
-                       $authMethod = $this->getMemberOption($this->memid, 'useAPOP') == 'yes' ? 'APOP' : 'USER';
-                       $this->_info(__LINE__ . ": $authMethod で認証を行います");
-               if(PEAR::isError($ret =& $pop3->login($this->user , $this->pass , $authMethod)) ){              
-                               $this->_warn(__LINE__ . ": 認証に失敗しました:" . $ret->getMessage() );
-                               $pop3->disconnect();
-                               continue;
-               }
-                       
-                       // 件数確認
-                       $num = $pop3->numMsg();
-                       $this->_info(__LINE__ . ": $num 件のメールがあります");
-                       if ($num == "0") {
-                               $pop3->disconnect();
-                               continue;
-                       }
-       
-                       // Msg取得
-                       for ($i = 1; $i <= $num; $i ++) {
-                               if(! $msg =& $pop3->getMsg($i) ){
-                                       $this->_warn(__LINE__ . ": メールの取得に失敗しました。");
-                               }
-                               
-                               $result = $this->addItemByMail($userId, $msg);
-                               if( $result ){
-                                       $this->_info(__LINE__ . ": メッセージを削除します");
-                                       $pop3->deleteMsg($i);
-                               }
-                       }
-                       //切断
-                       $pop3->disconnect();
-                       $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得終了しました");
-               }
-       }
-
-       function addItemByMail($userId, $msg) {
-               // メールデコード
-               $params['include_bodies'] = TRUE;
-               $params['decode_bodies']  = TRUE;
-               $params['decode_headers'] = TRUE;
-               $params['input'] = $msg;
-        if(PEAR::isError( $decodedMsg = Mail_mimeDecode::decode($params)) ){           
-                       $this->_warn(__LINE__ . ": メールデコードに失敗しました:" . $decodedMsg->getMessage() );
-                       return true;
-        }
-
-               // From:
-               if ( $decodedMsg->headers['from'] ) {
-                       $from = $this->_addr_search($decodedMsg->headers['from']);
-               }
-               if ( (! $from ) && $decodedMsg->headers['reply-to'] ) {
-                       $from = $this->_addr_search($decodedMsg->headers['reply-to']);
-               }
-               if ( (! $from ) && $decodedMsg->headers['return-path'] ) {
-                       $from = $this->_addr_search($decodedMsg->headers['return-path']);
-               }
-               if ( ! $from ){
-                       $this->_warn(__LINE__ . ": メールに送信者アドレスが見つかりません");
-                       return true;
-               }
-               $this->_info(__LINE__ . ": From($from)");
-
-               // 投稿可能かチェック
-               $from = strtolower(Trim($from));
-               if ( in_array($from, $this->accept) ) {
-                       $this->_info(__LINE__ . ": 投稿許可アドレスに含まれているので受付($from)");
-               } elseif( in_array( "*", $this->accept) ){
-                       $this->_info(__LINE__ . ": 投稿許可アドレスにワイルドカードが含まれているので受付($from)");
-               }else {
-                       if( $this->execMode == 0 ){
-                               $this->_info(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)。振り分け対応モードなので他のアカウントで取得が行われる場合があります。");
-                       } else {
-                               $this->_warn(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)");
-                       }
-                       // 互換の場合は true
-                       // 振り分けの場合は false
-                       return $this->execMode;
-               }
-
-               // Date:
-               $blog =& new BLOG($this->blogid);
-               
-               $timestamp = strtotime( trim($decodedMsg->headers['date']) );
-               if ($timestamp == -1){
-                       $this->_info(__LINE__ . ": Dateヘッダからのtimestamp取得に失敗しました。");
-                       $timestamp = 0; 
-               }
-               $timestamp = $blog->getCorrectTime($timestamp);
-
-               // Subject:
-               $subject = $this->_convert($decodedMsg->headers['subject']);
-               
-               // Subject: prefixチェック                          
-               if ( $this->acceptSubjectPrefix ){
-                       $this->_info(__LINE__ . ": 投稿許可SubjectPrefixがあります(prefix: $this->acceptSubjectPrefix)");      
-                       $pos = mb_strpos($subject, $this->acceptSubjectPrefix);
-                       if( $pos === 0 ){
-                               // prefix切り取り
-                               $subject = mb_substr($subject, mb_strlen($this->acceptSubjectPrefix) );
-                               $this->_info(__LINE__ . ": 投稿許可SubjectPrefixをみつけました(prefix削除後subject: $subject)");                          
-                       } else {
-                               $this->_warn(__LINE__ . ": 投稿許可SubjectPrefixがないので拒否します($subject)");
-                               return true;
-                       }
-               }
-               
-               // subject: オプション分割
-               if (preg_match('/'.$this->optionsKeyword.'/i', $subject)) {
-                       list ($subject, $option) = spliti($this->optionsKeyword, $subject, 2);
-
-                       $this->_info(__LINE__ . ": Subject($subject), Option($option)");
-                                               
-                       $option = '&'.$option;
-                       if (preg_match('/&' . $this->blogKeyword . '=([^&=]+)/i', $option, $word)) {
-                               $this->blogid = $word[1];
-                               $this->_info(__LINE__ . ': blogidを' . $this->blogid . 'で上書きします');
-                       }
-                       if (preg_match('/&' . $this->categoryKeyword . '=([^&=]+)/i', $option, $word)) {
-                               $this->categoryNameOrId = $word[1];
-                               $this->_info(__LINE__ . ': Categoryを' . $this->categoryNameOrId . 'で上書きします');
-                       }
-                       if (preg_match('/&' . $this->publishKeyword . '=([^&=]+)/i', $option, $word)) {
-                               $this->DefaultPublish = $word[1];
-                               $this->_info(__LINE__ . ($this->DefaultPublish ? ': 投稿を公開に上書きします' : ': 投稿を下書きに上書きします'));
-                       }
-               }
-               
-               // Subject: 空の場合
-               if( ! $subject = trim(htmlspecialchars($subject)) ){
-                       $subject = $this->nosubject;
-               }
-
-               // body
-               $text = "";     
-               if( strtolower($decodedMsg->ctype_primary) == "text" ){
-                       $this->_info(__LINE__ . ": single partメッセージです");
-                       $text = $this->_textPart($decodedMsg);
-               } elseif ( strtolower($decodedMsg->ctype_primary) == "multipart" ){
-                       $this->_info(__LINE__ . ": multipart partメッセージです");
-                       $texts = Array();
-                       $fileNames = Array();                   
-                       $this->_decodeMultiPart($decodedMsg->parts, $texts, $fileNames);
-                       
-                       $text = $texts['plain'];
-                       if( $texts['html'] ) $text = $texts['html'];
-               }
-               
-               if ($this->imgonly && (! $fileNames) ) {
-                       $this->_info(__LINE__ . ": 添付ファイルがないので書き込みません");
-                       return true;
-               }
-
-               if( $this->_isSpam($text) ){
-                       $this->_warn(__LINE__ . ": SPAMのため追加しません");
-                       return true;
-               }
-               
-               $body = '';
-               // body 生成
-               if ( ! $fileNames ) {
-                       // 添付ファイルがない場合のbody                     
-                       $vars = array (
-                               'body' => $text,
-                       );
-                       // textTpl
-                       $body .= TEMPLATE :: fill($this->getMemberOption($this->memid, 'textTpl'), $vars);
-               } else {
-                       // 添付ファイルがある場合のbody
-                       $lastFile = array_pop($fileNames);
-                       
-                       foreach( $fileNames as $filename ){
-                               $body .= $this->_imageHtml($filename);
-                       }
-                       
-                       $body .= $this->_imageHtml($lastFile, $text);
-               }
-                               
-               // item追加
-               $this->_info(__LINE__ . ": アイテム追加します");
-               
-               // bodyをbodyとmoreに分割
-               $more = '';
-               if( $this->moreDelimiter )
-                       list($body, $more) = spliti($this->moreDelimiter, $body, 2);
-                       
-               $body = trim($body);
-               $more = trim($more);
-                       
-               $this->_addDatedItem($this->blogid, $subject, $body, $more, 0, $timestamp, 0, $this->categoryNameOrId);
-               return true;
-       }
-       
-       function _decodeMultiPart($parts, &$texts, &$fileNames){
-               foreach($parts as $part){\r                      switch ( strtolower( $part->ctype_primary )){
-                               // multipart
-                               case 'multipart':
-                                       $this->_decodeMultiPart($part->parts, $texts, $fileNames);
-                                       break;
-                               //text part
-                               case 'text':
-                                       $this->_info(__LINE__ . ": text part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
-                                       $texts[$part->ctype_secondary] = $this->_textPart($part);
-                                       break;
-                               // imagepart
-                               case 'image':
-                               default:
-                                       $this->_info(__LINE__ . ": image/data part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
-                                       if( $fileName = $this->_imagePart($part) )
-                                               $fileNames[] = $fileName;
-                                       break;
-                       }
-               }
-       }
-
-       function _textPart(&$part){
-               $encoding = false;
-               if( $part->ctype_parameters )
-                       $encoding = $this->ctype_parameters['charset'];
-               
-               $text = $this->_convert($part->body, $encoding);
-               $text = strip_tags($text, $this->no_strip_tags);
-               
-               $blog =& new BLOG($this->blogid);
-               //blog設定で改行を<br />に置換onの場合
-               if ($blog->getSetting('bconvertbreaks')) { 
-                       if ( strtolower($part->ctype_secondary) == 'html' ) {
-                               //改行文字を削除、<br>タグを\nへ
-                               $text = str_replace("\r\n", "\r", $text);
-                               $text = str_replace("\r", "\n", $text);
-                               $text = str_replace("\n", "", $text);
-                               $text = str_replace("<br>", "\n", $text);
-                       }
-               }
-               return $text;
-       }
-       
-       function _imagePart(&$part){
-               if( !$this->prefixDate ){
-                       $this->prefixDate  = date('YmdHis');
-                       $this->fileCount = 0;
-               } else {
-                       $this->fileCount += 1;
-               }
-               $this->filePrefix = $this->prefixDate . sprintf('%02d', $this->fileCount);
-               
-               $filename = "";
-               if( $part->d_parameters ){
-                       $filename = $part->d_parameters['filename'];
-               } elseif( $part->ctype_parameters ){
-                       $filename = $part->ctype_parameters['name'];
-               } else {
-                       $filename = $part->ctype_secondary;
-               }
-
-               $filename = $this->_convert($filename);
-               $filename = $this->filePrefix . "-" . $filename;                
-               $this->_info(__LINE__ . ": FileName($filename)");
-               
-               // subtypeチェック
-               $size = strlen($part->body);
-               if( eregi($this->subtype, trim($part->ctype_secondary) )){
-                       // サイズ、拡張子チェック
-                       if ($size < $this->maxbyte && !eregi($this->viri, $filename)) {
-                                                               
-                               $fp = fopen($this->tmpdir.$filename, "w");
-                               fputs($fp, $part->body);
-                               fclose($fp);
-                                       
-                               $size = @getimagesize($this->tmpdir.$filename);
-                               //サムネイル作成する場合
-                               if ($this->thumb_ok && function_exists('ImageCreate')) {
-                                       //サムネイル作成する拡張子の場合
-                                       if ( preg_match("/$this->thumb_ext/i", $filename) ) {
-                                               if ($size[0] > $this->W || $size[1] > $this->H) {
-                                                       $this->_thumb_create($this->tmpdir.$filename, $this->W, $this->H, $this->thumb_dir);
-                                               }
-                                       }
-                               }
-                               return $filename;
-                       }
-                       $this->_warn(__LINE__ . ": 添付ファイルを無視します。(サイズ超過: $size B or 保存しないファイルに該当しています) [$part->ctype_primary/$part->ctype_secondary]");
-                       return false;
-               }
-               $this->_warn(__LINE__ . ": 添付ファイルを無視します。(subtypeチェック: $part->ctype_secondary が対応MIMEタイプに入っていますか?) [$part->ctype_primary/$part->ctype_secondary]");
-               return false;   
-       }
-       
-       function _imageHtml($filename, $body = ""){
-               global $CONF;
-               
-               $size = @getimagesize($this->tmpdir.$filename);
-               $thumb_size = @getimagesize($this->thumb_dir.$this->_getThumbFileName($filename));
-               $smallH = round($this->smallW / $size[0] * $size[1], 0);
-
-               $vars = array (
-                       'thumbW' => $thumb_size[0],
-                       'thumbH' => $thumb_size[1],
-                       'reductionW' => $this->smallW, 
-                       'reductionH' => $smallH,
-                       'sizeW' => $size[0],
-                       'sizeH' => $size[1],
-                       'body' => $body,
-                       'thumbUrl' => $this->thumb_collection.'/'.$this->_getThumbFileName($filename),
-                       'imageUrl' => $this->collection.'/'.$filename,
-                       'mediaUrl' => $CONF['MediaURL'],
-                       'fileName' => $filename
-               );
-               
-               // データファイルチェック
-               if( ! preg_match("/$this->imgExt/i", $filename) ){
-                       $this->_info(__LINE__ . ": 画像ファイルに該当しないので、データファイルテンプレートを使用します");
-                       return TEMPLATE :: fill($this->getMemberOption($this->memid, 'dataTpl'), $vars);
-               }
-
-               if ( $thumb_size[0] ) { //サムネイルがある場合のソース
-                       $this->_info(__LINE__ . ": サムネイルがあります");
-                       return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withThumbTpl'), $vars);
-               } else { //サムネイルがない場合のソース
-                       if ($size[0] > $this->smallW) { //縮小表示
-                               $this->_info(__LINE__ . ": サムネイルがありません、縮小表示します");
-                               return TEMPLATE :: fill($this->getMemberOption($this->memid, 'reductionTpl'), $vars);
-                       } else { //そのまま表示
-                               $this->_info(__LINE__ . ": サムネイルがありません");
-                               return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withoutThumbTpl'), $vars);
-                       }
-               }
-       }
-       
-       function _addDatedItem($blogid, $title, $body, $more, $closed, $timestamp, $future, $catNameOrId = "") {
-               // 1. ログイン======================
-               $mem = MEMBER :: createFromID($this->memid);
-
-               // 2. ブログ追加できるかチェック======================
-               if (!BLOG :: existsID($this->blogid)) {
-                       $this->_info(__LINE__ . ": 存在しないblogです");
-                       return false;
-               }
-               $this->_info(__LINE__ . ": blogidはOK!");
-
-               if (!$mem->isTeamMember($blogid)) {
-                       $this->_warn(__LINE__ . ": メンバーではありません");
-                       return false;
-               }
-               $this->_info(__LINE__ . ": メンバーチェックもok!");
-               
-               if (!trim($body)) {
-                       $this->_warn(__LINE__ . ": 空のアイテムは追加できません");
-                       return false;
-               }
-               $this->_info(__LINE__ . ": アイテムは空じゃないです");
-
-               // 3. 値の補完
-               $blog =& new BLOG($this->blogid);
-               if( $blog->isValidCategory($catNameOrId) ){
-                       // カテゴリIDとして有効なときはそのまま使う
-                       $catid = $catNameOrId;
-               } else {
-                       // カテゴリID ゲット (誤ったカテゴリID使用時はデフォを使用)
-                       $catid = $blog->getCategoryIdFromName($catNameOrId);
-               }
-               
-               $this->_info(__LINE__ . ": 追加するcatid: ".$catid);
-               if ($this->DefaultPublish) {
-                       $draft = 0;
-               } else {
-                       $draft = 1; //ドラフト追加
-                       $this->_info(__LINE__ . ": ドラフトで追加します");
-               }
-               if ($closed != 1)
-                       $closed = 0; //コメントを許可
-               $this->_info(__LINE__ . ": \$catid:".$catid.", \$draft:".$draft.", \$closed:".$closed);
-
-               // 4. blogに追加
-               $itemid = $blog->additem($catid, $title, $body, $more, $blogid, $mem->getID(), $timestamp, $closed, $draft);
-               
-               $this->_info(__LINE__ . ": itemid: $itemid");
-               return $itemid;
-       }
-       
-       function _isSpam($str){
-               global $manager;
-               if( $this->getOption('spamCheck') == 'yes' ){   
-                       $spamcheck = array (
-                               'type'          => 'Moblog',
-                               'data'          => $str,
-                               'return'        => true,
-                               'ipblock'   => false
-                       );
-                       $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));
-                       if (isset($spamcheck['result']) && $spamcheck['result'] == true)
-                               return true;
-               }
-               return false;
-       }
-}
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+/**\r
+  * NP_Moblog ($Revision: 1.136 $)\r
+  * by hsur ( http://blog.cles.jp/np_cles )\r
+  * $Id: NP_Moblog.php,v 1.136 2010/06/06 11:44:19 hsur Exp $\r
+  *\r
+  * Based on NP_HeelloWorld v0.8 \r
+  * http://nakahara21.com/?itemid=133\r
+*/\r
+\r
+/*\r
+  * Copyright (C) 2003 nakahara21 All rights reserved.\r
+  * Copyright (C) 2004-2010 cles All rights reserved.\r
+  *\r
+  * This program is free software; you can redistribute it and/or\r
+  * modify it under the terms of the GNU General Public License\r
+  * as published by the Free Software Foundation; either version 2\r
+  * of the License, or (at your option) any later version.\r
+  * \r
+  * This program is distributed in the hope that it will be useful,\r
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  * GNU General Public License for more details.\r
+  * \r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program; if not, write to the Free Software\r
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA\r
+  * \r
+  * In addition, as a special exception, mamio and cles gives\r
+  * permission to link the code of this program with those files in the PEAR\r
+  * library that are licensed under the PHP License (or with modified versions\r
+  * of those files that use the same license as those files), and distribute\r
+  * linked combinations including the two. You must obey the GNU General Public\r
+  * License in all respects for all of the code used other than those files in\r
+  * the PEAR library that are licensed under the PHP License. If you modify\r
+  * this file, you may extend this exception to your version of the file,\r
+  * but you are not obligated to do so. If you do not wish to do so, delete\r
+  * this exception statement from your version.\r
+*/\r
+\r
+if (!class_exists('NucleusPlugin')) exit;\r
+global $DIR_LIBS;\r
+require_once($DIR_LIBS . 'MEDIA.php');\r
+\r
+// クラスのロード\r
+require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');\r
+require_once('Net/POP3.php');\r
+require_once('Mail/mimeDecode.php');\r
+require_once('Mail/RFC822.php');\r
+\r
+// バージョンチェック\r
+$required = '4.3.0';\r
+if( ! version_compare(phpversion() , $required , '>=') ){\r
+       ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');\r
+}\r
+\r
+class NP_Moblog extends NucleusPlugin {\r
+\r
+       // name of plugin\r
+       function getName() {\r
+               return 'Moblog';\r
+       }\r
+\r
+       // author of plugin\r
+       function getAuthor() {\r
+               return 'hsur';\r
+       }\r
+\r
+       // an URL to the plugin website\r
+       // can also be of the form mailto:foo@bar.com\r
+       function getURL() {\r
+               return 'http://blog.cles.jp/np_cles/category/31/subcatid/2';\r
+       }\r
+\r
+       // version of the plugin\r
+       function getVersion() {\r
+               return '1.17.0';\r
+       }\r
+       \r
+       function hasAdminArea() {\r
+               return 1;\r
+       }\r
+       \r
+       function getEventList() {\r
+               return array('PrePluginOptionsEdit');\r
+       }\r
+       \r
+       function event_PrePluginOptionsEdit(&$data) {\r
+               switch($data['context']){\r
+                       case 'member':\r
+                               // idandcat\r
+                               $m =& MEMBER :: createFromID($data['contextid']);\r
+                               $trimChar = array(\r
+                                       '=' => '',\r
+                                       '|' => '',\r
+                                       ';' => '',\r
+                               );\r
+                               \r
+                               $blogs = Array();\r
+                               $res = sql_query('SELECT bnumber, bname FROM '.sql_table('blog'));\r
+                               while( $o = mysql_fetch_object($res) ){\r
+                                        if( $m->isTeamMember($o->bnumber) ){\r
+                                               $blogs[$o->bnumber] = $o->bname;\r
+                                        }\r
+                               }\r
+                               \r
+                               $idandcatTypeInfo = '';\r
+                               foreach($blogs as $blogid => $blogname){\r
+                                       $res = sql_query('SELECT catid, cname FROM '.sql_table('category').' WHERE cblog='.$blogid);\r
+                                       if( @mysql_num_rows($res) > 0) {\r
+                                               while( $o = mysql_fetch_object($res) ){\r
+                                                       if($idandcatTypeInfo)\r
+                                                               $idandcatTypeInfo .= '|';\r
+                                                       $o->cname = strtr($o->cname, $trimChar);\r
+                                                       $blogname = strtr($blogname, $trimChar);\r
+                                                       $idandcatTypeInfo .= "{$o->cname} ({$blogname})|{$blogid},{$o->catid}";\r
+                                               }\r
+                                       }\r
+                               }\r
+                               if( ! $idandcatTypeInfo ){\r
+                                       $idandcatTypeInfo = "!!投稿可能なblogがありません!!|0,0";\r
+                               }\r
+                               \r
+                               // collection & thumb_col\r
+                               $collections = MEDIA::getCollectionList();\r
+                               $mid = intval($m->getID());\r
+                               $collections[$mid] = 'デフォルト(useridディレクトリ)';\r
+                               \r
+                               $collectionTypeInfo = '';\r
+                               foreach( $collections as $collection => $name ){\r
+                                       if($collectionTypeInfo) $collectionTypeInfo .= '|';\r
+                                       $name = strtr($name, $trimChar);\r
+                                       $collection = strtr($collection, $trimChar);\r
+                                       $collectionTypeInfo .= "{$name}|{$collection}";\r
+                               }\r
+                               \r
+                               // set options\r
+                               foreach($data['options'] as $oid => $option ){\r
+                                       switch($data['options'][$oid]['name']){\r
+                                               case 'idandcat':\r
+                                                       $data['options'][$oid]['typeinfo'] = $idandcatTypeInfo;\r
+                                                       break;\r
+                                               case 'collection':\r
+                                               case 'thumb_col':\r
+                                                       $data['options'][$oid]['typeinfo'] = $collectionTypeInfo;\r
+                                                       break;\r
+                                       }\r
+                               }\r
+                                                               \r
+                               break;\r
+                       default:\r
+                               // nothing      \r
+               }\r
+       }\r
+       \r
+       function install() {\r
+               $this->createMemberOption('enable', 'プラグインを有効にするか?', 'yesno', 'no');\r
+\r
+               $this->createMemberOption('host', 'POP3 ホスト名', 'text', 'localhost');\r
+               $this->createMemberOption('port', 'POP3 ポート', 'text', '110', 'numerical=true');\r
+               $this->createMemberOption('user', 'POP3 ユーザー名', 'text', '');\r
+               $this->createMemberOption('pass', 'POP3 パスワード', 'password', '');\r
+               $this->createMemberOption('useAPOP', 'APOPを使用するか?', 'yesno', 'no');\r
+\r
+               $this->createMemberOption('idandcat', 'Nucleusカテゴリ(Blog)', 'select', '', '');\r
+\r
+               $this->createMemberOption('collection', '画像を保存するディレクトリ', 'select', '', '');\r
+               $this->createMemberOption('thumb_col', 'サムネイルをを保存するディレクトリ', 'select', '', '');\r
+\r
+               $this->createMemberOption('imgonly', 'イメージ添付メールのみ追加?', 'yesno', 'no');\r
+               $this->createMemberOption('DefaultPublish', 'デフォルトで公開するか?', 'yesno', 'no');\r
+\r
+               $this->createMemberOption('optionsKeyword', 'オプション記述開始の区切り文字', 'text', '@');\r
+               $this->createMemberOption('blogKeyword', 'オプションでblogidを指定する場合のキー', 'text', 'b');\r
+               $this->createMemberOption('categoryKeyword', 'オプションでカテゴリを指定する場合のキー', 'text', 'c');\r
+               $this->createMemberOption('publishKeyword', 'オプションでストレートにpublish指定する場合のキー', 'text', 's');\r
+\r
+               $this->createMemberOption('moreDelimiter', '追記にする場合の区切り文字(利用しない場合は空欄)', 'text', '');\r
+\r
+               $this->createMemberOption('accept', '投稿許可アドレス(複数の場合改行で区切ってください)', 'textarea', '');\r
+               $this->createMemberOption('acceptSubjectPrefix','投稿許可SubjectPrefix(制限無しの場合は空欄)','text','');\r
+\r
+               $this->createMemberOption('nosubject', '件名がないときの題名', 'text', '');\r
+               $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>');\r
+               $this->createMemberOption('maxbyte', '最大添付量(B)', 'text', '300000', 'numerical=true');\r
+               $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');\r
+               $this->createMemberOption('viri', '保存しないファイル(正規表現)', 'text', '.+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$');\r
+               $this->createMemberOption('imgExt', '画像ファイルの拡張子(正規表現)', 'text', '.+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$');\r
+\r
+               $this->createMemberOption('thumb_ok', 'サムネイルを使用する?', 'yesno', 'yes');\r
+               $this->createMemberOption('W', 'サムネイルの大きさ(Width)', 'text', '120', 'numerical=true');\r
+               $this->createMemberOption('H', 'サムネイルの大きさ(Hight)', 'text', '120', 'numerical=true');\r
+               $this->createMemberOption('thumb_ext', 'サムネイルを作る対象画像', 'text', '.+\.jpe?g$|.+\.png$');\r
+               $this->createMemberOption('smallW', 'アイテム内に表示する画像の最大横幅', 'text', '120', 'numerical=true');\r
+\r
+               $this->createMemberOption('textTpl', 'テキストテンプレート', 'textarea', '<%body%>');\r
+               $this->createMemberOption('withThumbTpl', 'サムネイル付きテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%thumbUrl%>|<%thumbW%>|<%thumbH%>|)%></a></div><%body%>');\r
+               $this->createMemberOption('withoutThumbTpl', 'サムネイルなしテンプレート', 'textarea', '<div class="leftbox"><%image(<%imageUrl%>|<%sizeW%>|<%sizeH%>|)%></div><%body%>');\r
+               $this->createMemberOption('reductionTpl', 'サムネイルなしテンプレート(縮小)', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%imageUrl%>|<%reductionW%>|<%reductionH%>|)%></a></div><%body%>');\r
+               $this->createMemberOption('dataTpl', 'データファイルテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%fileName%></a></div><%body%>');\r
+               \r
+               $this->createOption('execMode', '動作モード', 'select', '1', '振分対応モード|0|互換モード|1');\r
+               $this->createOption('spamCheck', 'SPAMチェックを有効にする', 'yesno', 'no');\r
+               \r
+               $this->createOption('interval', 'メール取得の間隔(秒)', 'text', '600', 'numerical=true');\r
+               $this->createOption('nextUpdate', '次回更新時刻(変更できません)', 'text', '-', 'access=readonly');\r
+               $this->createOption('lastUpdate', '最終更新時刻(UnixTimeStamp,)', 'text', '0', 'access=hidden');\r
+               $this->createOption('debug', 'ログを出力を行うか?', 'yesno', 'no');\r
+       }\r
+       \r
+       function unInstall() {}\r
+       function getMinNucleusVersion() { return 320; }\r
+       function getMinNucleusPatchLevel() { return 0; }\r
+\r
+       // a description to be shown on the installed plugins listing\r
+       function getDescription() {\r
+               return '[$Revision: 1.136 $]<br />メールを拾ってアイテムを追加します。&lt;%Moblog%&gt;の記述のあるスキンを適用するページを開くと実行されます。<br />\r
+                               &lt;%Moblog(link)%&gt;と記入することでメールを取得するためのリンクを表示することができます(要ログイン)<br />\r
+                               個人ごとに設定ができるようになりましたので「あなたの設定」か「メンバー管理」から設定を行ってください。';\r
+       }\r
+\r
+       function supportsFeature($what) {\r
+               switch ($what) {\r
+                       case 'SqlTablePrefix' :\r
+                       case 'HelpPage':\r
+                               return 1;\r
+                       default :\r
+                               return 0;\r
+               }\r
+       }\r
+\r
+       function _info($msg) {\r
+               if ($this->getOption('debug') == 'yes') {\r
+                       ACTIONLOG :: add(INFO, 'Moblog: '.$msg);\r
+               }\r
+       }\r
+\r
+       function _warn($msg) {\r
+               ACTIONLOG :: add(WARNING, 'Moblog: '.$msg);\r
+       }\r
+\r
+       function _getEnableUserId() {\r
+               $userOptions = $this->getAllMemberOptions('enable');\r
+               $userIds = Array ();\r
+               foreach( $userOptions as $userId => $value ){\r
+                       if( $value == 'yes') $userIds[] = $userId;\r
+               }\r
+               return $userIds;\r
+       }\r
+\r
+       function _initMediaDirByUserId($userId) {\r
+               global $DIR_MEDIA;\r
+\r
+               $this->_info(__LINE__ . ": ユーザ($userId)の初期設定");\r
+               $this->memid = $userId;\r
+\r
+               /*-- 受信メールサーバーの設定--*/\r
+               $this->host = $this->getMemberOption($userId, 'host');\r
+               $this->port = $this->getMemberOption($userId, 'port');\r
+               $this->user = $this->getMemberOption($userId, 'user');\r
+               $this->pass = $this->getMemberOption($userId, 'pass');  \r
+\r
+               // メールでアイテムを追加するblogのID\r
+               $idandcat = $this->getMemberOption($userId, 'idandcat');\r
+               list($this->blogid, $this->categoryNameOrId) = explode(",", $idandcat);\r
+               \r
+               $this->imgonly = ($this->getMemberOption($userId, 'imgonly') == 'yes') ? 1 : 0;\r
+               $this->DefaultPublish = ($this->getMemberOption($userId, 'DefaultPublish') == 'yes') ? 1 : 0;\r
+\r
+               /*-- メールのタイトルに各種オプションを含める場合の設定--*/\r
+               $this->optionsKeyword = $this->getMemberOption($userId, 'optionsKeyword');\r
+               $this->blogKeyword = $this->getMemberOption($userId, 'blogKeyword');\r
+               $this->categoryKeyword = $this->getMemberOption($userId, 'categoryKeyword');\r
+               $this->publishKeyword = $this->getMemberOption($userId, 'publishKeyword');\r
+\r
+               // 投稿許可アドレス\r
+               $this->accept = explode("\n", $this->getMemberOption($userId, 'accept'));\r
+               $this->accept = Array_Map("Trim", $this->accept);\r
+               $this->accept = Array_Map("strtolower", $this->accept);\r
+               foreach( $this->accept as $mailAddr ){\r
+                       $this->_info(__LINE__ . "許可アドレス user:$userId, $mailAddr");\r
+               }               \r
+\r
+               // 投稿許可SubjectPrefix\r
+               $this->acceptSubjectPrefix = Trim($this->getMemberOption($userId, 'acceptSubjectPrefix'));\r
+               \r
+               // 追記の区切り\r
+               $this->moreDelimiter = Trim($this->getMemberOption($userId, 'moreDelimiter'));\r
+               \r
+               // 件名がないときの題名\r
+               $this->nosubject = $this->getMemberOption($userId, 'nosubject');\r
+               $this->no_strip_tags = $this->getMemberOption($userId, 'no_strip_tags');\r
+\r
+               // 最大添付量(バイト・1ファイルにつき)※超えるものは保存しない\r
+               $this->maxbyte = $this->getMemberOption($userId, 'maxbyte');\r
+               $this->subtype = $this->getMemberOption($userId, 'subtype');\r
+               $this->viri = $this->getMemberOption($userId, 'viri');\r
+               $this->imgExt = $this->getMemberOption($userId, 'imgExt');\r
+\r
+               // サムネイル\r
+               $this->thumb_ok = ($this->getMemberOption($userId, 'thumb_ok') == 'yes') ? 1 : 0;\r
+               $this->W = $this->getMemberOption($userId, 'W');\r
+               $this->H = $this->getMemberOption($userId, 'H');\r
+               $this->thumb_ext = $this->getMemberOption($userId, 'thumb_ext');\r
+               $this->smallW = $this->getMemberOption($userId, 'smallW');\r
+               \r
+               // 画像保存ディレクトリ\r
+               $collections = MEDIA::getCollectionList();\r
+               $collection = $this->getMemberOption($userId, 'collection');\r
+               if( $collection && isset($collections[$collection]) ){\r
+                       $this->collection = $collection;\r
+               } else {\r
+                       $this->_info(__LINE__ . ": 画像保存ディレクトリが正しくありません。デフォルトを使用します");\r
+                       $this->collection = $this->memid;\r
+               }\r
+               \r
+               $this->tmpdir = $DIR_MEDIA.$this->collection.'/';\r
+               $this->_info(__LINE__ . ": 画像保存ディレクトリ: $this->tmpdir");\r
+               \r
+               if (!@is_dir($this->tmpdir)) {\r
+                       $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないので、ディレクトリを作成します。");\r
+                       $oldumask = umask(0000);\r
+                       if (!@mkdir($this->tmpdir, 0777))\r
+                               return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリの作成に失敗しました。パーミッションを確認してください。");\r
+                       umask($oldumask);                               \r
+               } \r
+\r
+               if (!is_writable($this->tmpdir)) {\r
+                       $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないか、書き込み可能になっていません");\r
+               }\r
+               \r
+               // サムネイル保存ディレクトリ\r
+               $thumb_collection = $this->getMemberOption($userId, 'thumb_col');\r
+               if( $collection && isset($collections[$thumb_collection]) ){\r
+                       $this->thumb_collection = $thumb_collection;\r
+               } else {\r
+                       $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリが正しくありません。デフォルトを使用します");\r
+                       $this->thumb_collection = $this->memid;\r
+               }\r
+\r
+               $this->thumb_dir = $DIR_MEDIA.$this->thumb_collection.'/';\r
+               $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリ: $this->thumb_dir");\r
+               \r
+               if (!@is_dir($this->thumb_dir)) {\r
+               $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないので、ディレクトリを作成します。");\r
+                       $oldumask = umask(0000);\r
+                       if (!@mkdir($this->thumb_dir, 0777))\r
+                               return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリの作成に失敗しました。パーミッションを確認してください。");\r
+                       umask($oldumask);                               \r
+               } \r
+               \r
+               if (!is_writable($this->thumb_dir)) {\r
+                       $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないか、書き込み可能になっていません");\r
+               }\r
+       }\r
+\r
+       function _convert($str, $input_encoding = false) {\r
+               if( ! $input_encoding ){\r
+                       $input_encoding = "ISO-2022-JP,ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-2022-JP";\r
+                       $encoding = mb_detect_encoding($str, $input_encoding);\r
+                       if( ! $encoding )\r
+                               $input_encoding = "ISO-2022-JP";\r
+               }\r
+               return mb_convert_encoding($str, _CHARSET, $input_encoding);\r
+       }\r
+       \r
+       function _addr_search($str) {\r
+               if( PEAR::isError($addresses = Mail_RFC822::parseAddressList($str)) ){\r
+                       return false;\r
+               }\r
+               $addr = array_shift($addresses);\r
+               if($addr)\r
+                       return $addr->mailbox . "@" .$addr->host;\r
+               return false;\r
+       }\r
+\r
+       function _thumb_create($src, $W, $H, $thumb_dir = "./") {\r
+                       // 画像の幅と高さとタイプを取得\r
+                               $size = GetImageSize($src);\r
+               switch ($size[2]) {\r
+                       case 1 :\r
+                               return false;\r
+                               break;\r
+                       case 2 :\r
+                               $im_in = @ImageCreateFromJPEG($src);\r
+                               break;\r
+                       case 3 :\r
+                               $im_in = @ImageCreateFromPNG($src);\r
+                               break;\r
+               }\r
+               if (!$im_in) {\r
+                       $this->_warn(__LINE__ . ": GDをサポートしていないか、ソースが見つかりません<br>phpinfo()でGDオプションを確認してください");\r
+                       return false;\r
+               }\r
+               // リサイズ\r
+               if ($size[0] > $W || $size[1] > $H) {\r
+                       $key_w = $W / $size[0];\r
+                       $key_h = $H / $size[1];\r
+                       ($key_w < $key_h) ? $keys = $key_w : $keys = $key_h;\r
+                       $out_w = $size[0] * $keys;\r
+                       $out_h = $size[1] * $keys;\r
+               } else {\r
+                       $out_w = $size[0];\r
+                       $out_h = $size[1];\r
+               }\r
+               // 出力画像(サムネイル)のイメージを作成し、元画像をコピーします。(GD2.0用)\r
+               $im_out = ImageCreateTrueColor($out_w, $out_h);\r
+               $resize = ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $size[0], $size[1]);\r
+\r
+               // サムネイル画像をブラウザに出力、保存\r
+               $filename = substr($src, strrpos($src, "/") + 1);\r
+               ImageJPEG($im_out, $thumb_dir.$this->_getThumbFileName($filename)); //jpgサムネイル作成\r
+               // 作成したイメージを破棄\r
+               ImageDestroy($im_in);\r
+               ImageDestroy($im_out);\r
+               \r
+               $this->_info(__LINE__ . ": サムネイルを作成しました" . $thumb_dir.$this->_getThumbFileName($filename));\r
+               return true;\r
+       }\r
+       \r
+       function _getThumbFileName($filename){\r
+               $filename = substr($filename, 0, strrpos($filename, "."));\r
+               return $filename."-small.jpg";\r
+       }\r
+\r
+       function _getExecuteLink() {\r
+               global $CONF;\r
+               return $CONF['ActionURL'].'?action=plugin&amp;name=Moblog&amp;type=execute';\r
+       }\r
+\r
+       function _checkLastupdate() {\r
+               $now = time();\r
+               $lastUpdate = $this->getOption('lastUpdate');\r
+               $interval = $this->getOption('interval');\r
+\r
+               if ($lastUpdate + $interval < $now) {\r
+                       $this->setOption('lastUpdate', $now);\r
+                       $this->setOption('nextUpdate', date("Y-m-d H:i:s", $lastUpdate + $interval));\r
+                       $this->_info(__LINE__ . ": 更新します。");\r
+                       return true;\r
+               }\r
+               $this->_info(__LINE__ . ": 更新しません。次回更新は".date("Y-m-d H:i:s", $lastUpdate + $interval)."以降です。");\r
+               return false;\r
+       }\r
+\r
+       function doSkinVar($skinType, $type = "") {\r
+               global $member;\r
+               switch ($type) {\r
+                       case '' :\r
+                       case 'execute' :\r
+                               if ( $this->_checkLastupdate() )\r
+                                       $this->execute();\r
+                               break;\r
+\r
+                       case 'link' :\r
+                               if ( $member->isLoggedIn() )\r
+                                       echo '<a href="'.$this->_getExecuteLink().'">Add Item by Mail</a>';\r
+                               break;\r
+               }\r
+       } //end of function doSkinVar($skinType)\r
+\r
+       function doAction($type) {\r
+               global $member;\r
+\r
+               switch ($type) {\r
+                       case '' :\r
+                       case 'execute' :\r
+                               if (!$member->isLoggedIn())\r
+                                       return "ログインが必要です";\r
+                               $this->execute();\r
+                               header('Location: ' . serverVar('HTTP_REFERER'));\r
+                               break;\r
+\r
+                       default :\r
+                               return 'アクションが定義されていません: '.$type;\r
+               }\r
+       }\r
+\r
+       function execute() {\r
+               $this->execMode = intval($this->getOption('execMode'));\r
+               if( $this->execMode == 0 ){\r
+                       // false\r
+                       $this->_info(__LINE__ . ": 振分対応モードで動作します。");\r
+               } elseif ( $this->execMode == 1 ) {\r
+                       // true\r
+                       $this->_info(__LINE__ . ": 互換モードで動作します");                 \r
+               }\r
+               \r
+               $enabledUserIds = $this->_getEnableUserId();\r
+               foreach ($enabledUserIds as $userId) {\r
+                       $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得開始します");\r
+                       $this->_initMediaDirByUserId($userId);\r
+                                               \r
+                       // 接続\r
+                       $pop3 =& new Net_POP3();\r
+                       $pop3->_timeout = 10;\r
+                       \r
+                       if( ! $pop3->connect($this->host, $this->port) ){\r
+                               $this->_warn(__LINE__ . ": POPサーバーに接続できません");\r
+                               continue;\r
+                       }\r
+                       \r
+                       // 認証\r
+                       $authMethod = $this->getMemberOption($this->memid, 'useAPOP') == 'yes' ? 'APOP' : 'USER';\r
+                       $this->_info(__LINE__ . ": $authMethod で認証を行います");\r
+               if(PEAR::isError($ret =& $pop3->login($this->user , $this->pass , $authMethod)) ){              \r
+                               $this->_warn(__LINE__ . ": 認証に失敗しました:" . $ret->getMessage() );\r
+                               $pop3->disconnect();\r
+                               continue;\r
+               }\r
+                       \r
+                       // 件数確認\r
+                       $num = $pop3->numMsg();\r
+                       $this->_info(__LINE__ . ": $num 件のメールがあります");\r
+                       if ($num == "0") {\r
+                               $pop3->disconnect();\r
+                               continue;\r
+                       }\r
+       \r
+                       // Msg取得\r
+                       for ($i = 1; $i <= $num; $i ++) {\r
+                               if(! $msg =& $pop3->getMsg($i) ){\r
+                                       $this->_warn(__LINE__ . ": メールの取得に失敗しました。");\r
+                               }\r
+                               \r
+                               $result = $this->addItemByMail($userId, $msg);\r
+                               if( $result ){\r
+                                       $this->_info(__LINE__ . ": メッセージを削除します");\r
+                                       $pop3->deleteMsg($i);\r
+                               }\r
+                       }\r
+                       //切断\r
+                       $pop3->disconnect();\r
+                       $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得終了しました");\r
+               }\r
+       }\r
+\r
+       function addItemByMail($userId, $msg) {\r
+               // メールデコード\r
+               $params['include_bodies'] = TRUE;\r
+               $params['decode_bodies']  = TRUE;\r
+               $params['decode_headers'] = TRUE;\r
+               $params['input'] = $msg;\r
+        if(PEAR::isError( $decodedMsg = Mail_mimeDecode::decode($params)) ){           \r
+                       $this->_warn(__LINE__ . ": メールデコードに失敗しました:" . $decodedMsg->getMessage() );\r
+                       return true;\r
+        }\r
+\r
+               // From:\r
+               if ( $decodedMsg->headers['from'] ) {\r
+                       $from = $this->_addr_search($decodedMsg->headers['from']);\r
+               }\r
+               if ( (! $from ) && $decodedMsg->headers['reply-to'] ) {\r
+                       $from = $this->_addr_search($decodedMsg->headers['reply-to']);\r
+               }\r
+               if ( (! $from ) && $decodedMsg->headers['return-path'] ) {\r
+                       $from = $this->_addr_search($decodedMsg->headers['return-path']);\r
+               }\r
+               if ( ! $from ){\r
+                       $this->_warn(__LINE__ . ": メールに送信者アドレスが見つかりません");\r
+                       return true;\r
+               }\r
+               $this->_info(__LINE__ . ": From($from)");\r
+\r
+               // 投稿可能かチェック\r
+               $from = strtolower(Trim($from));\r
+               if ( in_array($from, $this->accept) ) {\r
+                       $this->_info(__LINE__ . ": 投稿許可アドレスに含まれているので受付($from)");\r
+               } elseif( in_array( "*", $this->accept) ){\r
+                       $this->_info(__LINE__ . ": 投稿許可アドレスにワイルドカードが含まれているので受付($from)");\r
+               }else {\r
+                       if( $this->execMode == 0 ){\r
+                               $this->_info(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)。振り分け対応モードなので他のアカウントで取得が行われる場合があります。");\r
+                       } else {\r
+                               $this->_warn(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)");\r
+                       }\r
+                       // 互換の場合は true\r
+                       // 振り分けの場合は false\r
+                       return $this->execMode;\r
+               }\r
+\r
+               // Date:\r
+               $blog =& new BLOG($this->blogid);\r
+               \r
+               $timestamp = strtotime( trim($decodedMsg->headers['date']) );\r
+               if ($timestamp == -1){\r
+                       $this->_info(__LINE__ . ": Dateヘッダからのtimestamp取得に失敗しました。");\r
+                       $timestamp = 0; \r
+               }\r
+               $timestamp = $blog->getCorrectTime($timestamp);\r
+\r
+               // Subject:\r
+               $subject = $this->_convert($decodedMsg->headers['subject']);\r
+               \r
+               // Subject: prefixチェック                          \r
+               if ( $this->acceptSubjectPrefix ){\r
+                       $this->_info(__LINE__ . ": 投稿許可SubjectPrefixがあります(prefix: $this->acceptSubjectPrefix)");      \r
+                       $pos = mb_strpos($subject, $this->acceptSubjectPrefix);\r
+                       if( $pos === 0 ){\r
+                               // prefix切り取り\r
+                               $subject = mb_substr($subject, mb_strlen($this->acceptSubjectPrefix) );\r
+                               $this->_info(__LINE__ . ": 投稿許可SubjectPrefixをみつけました(prefix削除後subject: $subject)");                          \r
+                       } else {\r
+                               $this->_warn(__LINE__ . ": 投稿許可SubjectPrefixがないので拒否します($subject)");\r
+                               return true;\r
+                       }\r
+               }\r
+               \r
+               // subject: オプション分割\r
+               if (preg_match('/'.$this->optionsKeyword.'/i', $subject)) {\r
+                       list ($subject, $option) = spliti($this->optionsKeyword, $subject, 2);\r
+\r
+                       $this->_info(__LINE__ . ": Subject($subject), Option($option)");\r
+                                               \r
+                       $option = '&'.$option;\r
+                       if (preg_match('/&' . $this->blogKeyword . '=([^&=]+)/i', $option, $word)) {\r
+                               $this->blogid = $word[1];\r
+                               $this->_info(__LINE__ . ': blogidを' . $this->blogid . 'で上書きします');\r
+                       }\r
+                       if (preg_match('/&' . $this->categoryKeyword . '=([^&=]+)/i', $option, $word)) {\r
+                               $this->categoryNameOrId = $word[1];\r
+                               $this->_info(__LINE__ . ': Categoryを' . $this->categoryNameOrId . 'で上書きします');\r
+                       }\r
+                       if (preg_match('/&' . $this->publishKeyword . '=([^&=]+)/i', $option, $word)) {\r
+                               $this->DefaultPublish = $word[1];\r
+                               $this->_info(__LINE__ . ($this->DefaultPublish ? ': 投稿を公開に上書きします' : ': 投稿を下書きに上書きします'));\r
+                       }\r
+               }\r
+               \r
+               // Subject: 空の場合\r
+               if( ! $subject = trim(htmlspecialchars($subject, ENT_QUOTES)) ){\r
+                       $subject = $this->nosubject;\r
+               }\r
+\r
+               // body\r
+               $text = "";     \r
+               if( strtolower($decodedMsg->ctype_primary) == "text" ){\r
+                       $this->_info(__LINE__ . ": single partメッセージです");\r
+                       $text = $this->_textPart($decodedMsg);\r
+               } elseif ( strtolower($decodedMsg->ctype_primary) == "multipart" ){\r
+                       $this->_info(__LINE__ . ": multipart partメッセージです");\r
+                       $texts = Array();\r
+                       $fileNames = Array();                   \r
+                       $this->_decodeMultiPart($decodedMsg->parts, $texts, $fileNames);\r
+                       \r
+                       $text = $texts['plain'];\r
+                       if( $texts['html'] ) $text = $texts['html'];\r
+               }\r
+               \r
+               if ($this->imgonly && (! $fileNames) ) {\r
+                       $this->_info(__LINE__ . ": 添付ファイルがないので書き込みません");\r
+                       return true;\r
+               }\r
+\r
+               if( $this->_isSpam($text) ){\r
+                       $this->_warn(__LINE__ . ": SPAMのため追加しません");\r
+                       return true;\r
+               }\r
+               \r
+               $body = '';\r
+               // body 生成\r
+               if ( ! $fileNames ) {\r
+                       // 添付ファイルがない場合のbody                     \r
+                       $vars = array (\r
+                               'body' => $text,\r
+                       );\r
+                       // textTpl\r
+                       $body .= TEMPLATE :: fill($this->getMemberOption($this->memid, 'textTpl'), $vars);\r
+               } else {\r
+                       // 添付ファイルがある場合のbody\r
+                       $lastFile = array_pop($fileNames);\r
+                       \r
+                       foreach( $fileNames as $filename ){\r
+                               $body .= $this->_imageHtml($filename);\r
+                       }\r
+                       \r
+                       $body .= $this->_imageHtml($lastFile, $text);\r
+               }\r
+                               \r
+               // item追加\r
+               $this->_info(__LINE__ . ": アイテム追加します");\r
+               \r
+               // bodyをbodyとmoreに分割\r
+               $more = '';\r
+               if( $this->moreDelimiter )\r
+                       list($body, $more) = spliti($this->moreDelimiter, $body, 2);\r
+                       \r
+               $body = trim($body);\r
+               $more = trim($more);\r
+                       \r
+               $this->_addDatedItem($this->blogid, $subject, $body, $more, 0, $timestamp, 0, $this->categoryNameOrId);\r
+               return true;\r
+       }\r
+       \r
+       function _decodeMultiPart($parts, &$texts, &$fileNames){\r
+               foreach($parts as $part){\r                      switch ( strtolower( $part->ctype_primary )){\r
+                               // multipart\r
+                               case 'multipart':\r
+                                       $this->_decodeMultiPart($part->parts, $texts, $fileNames);\r
+                                       break;\r
+                               //text part\r
+                               case 'text':\r
+                                       $this->_info(__LINE__ . ": text part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");\r
+                                       $texts[$part->ctype_secondary] = $this->_textPart($part);\r
+                                       break;\r
+                               // imagepart\r
+                               case 'image':\r
+                               default:\r
+                                       $this->_info(__LINE__ . ": image/data part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");\r
+                                       if( $fileName = $this->_imagePart($part) )\r
+                                               $fileNames[] = $fileName;\r
+                                       break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       function _textPart(&$part){\r
+               $encoding = false;\r
+               if( $part->ctype_parameters )\r
+                       $encoding = $this->ctype_parameters['charset'];\r
+               \r
+               $text = $this->_convert($part->body, $encoding);\r
+               $text = strip_tags($text, $this->no_strip_tags);\r
+               \r
+               $blog =& new BLOG($this->blogid);\r
+               //blog設定で改行を<br />に置換onの場合\r
+               if ($blog->getSetting('bconvertbreaks')) { \r
+                       if ( strtolower($part->ctype_secondary) == 'html' ) {\r
+                               //改行文字を削除、<br>タグを\nへ\r
+                               $text = str_replace("\r\n", "\r", $text);\r
+                               $text = str_replace("\r", "\n", $text);\r
+                               $text = str_replace("\n", "", $text);\r
+                               $text = str_replace("<br>", "\n", $text);\r
+                       }\r
+               }\r
+               return $text;\r
+       }\r
+       \r
+       function _imagePart(&$part){\r
+               if( !$this->prefixDate ){\r
+                       $this->prefixDate  = date('YmdHis');\r
+                       $this->fileCount = 0;\r
+               } else {\r
+                       $this->fileCount += 1;\r
+               }\r
+               $this->filePrefix = $this->prefixDate . sprintf('%02d', $this->fileCount);\r
+               \r
+               $filename = "";\r
+               if( $part->d_parameters ){\r
+                       $filename = $part->d_parameters['filename'];\r
+               } elseif( $part->ctype_parameters ){\r
+                       $filename = $part->ctype_parameters['name'];\r
+               } else {\r
+                       $filename = $part->ctype_secondary;\r
+               }\r
+\r
+               $filename = $this->_convert($filename);\r
+               $filename = $this->filePrefix . "-" . $filename;                \r
+               $this->_info(__LINE__ . ": FileName($filename)");\r
+               \r
+               // subtypeチェック\r
+               $size = strlen($part->body);\r
+               if( preg_match("/".$this->subtype."/i", trim($part->ctype_secondary) )){\r
+                       // サイズ、拡張子チェック\r
+                       if ($size < $this->maxbyte && preg_match("/".$this->viri.'/i', $filename)) {\r
+                                                               \r
+                               $fp = fopen($this->tmpdir.$filename, "w");\r
+                               fputs($fp, $part->body);\r
+                               fclose($fp);\r
+                                       \r
+                               $size = @getimagesize($this->tmpdir.$filename);\r
+                               //サムネイル作成する場合\r
+                               if ($this->thumb_ok && function_exists('ImageCreate')) {\r
+                                       //サムネイル作成する拡張子の場合\r
+                                       if ( preg_match("/$this->thumb_ext/i", $filename) ) {\r
+                                               if ($size[0] > $this->W || $size[1] > $this->H) {\r
+                                                       $this->_thumb_create($this->tmpdir.$filename, $this->W, $this->H, $this->thumb_dir);\r
+                                               }\r
+                                       }\r
+                               }\r
+                               return $filename;\r
+                       }\r
+                       $this->_warn(__LINE__ . ": 添付ファイルを無視します。(サイズ超過: $size B or 保存しないファイルに該当しています) [$part->ctype_primary/$part->ctype_secondary]");\r
+                       return false;\r
+               }\r
+               $this->_warn(__LINE__ . ": 添付ファイルを無視します。(subtypeチェック: $part->ctype_secondary が対応MIMEタイプに入っていますか?) [$part->ctype_primary/$part->ctype_secondary]");\r
+               return false;   \r
+       }\r
+       \r
+       function _imageHtml($filename, $body = ""){\r
+               global $CONF;\r
+               \r
+               $size = @getimagesize($this->tmpdir.$filename);\r
+               $thumb_size = @getimagesize($this->thumb_dir.$this->_getThumbFileName($filename));\r
+               $smallH = round($this->smallW / $size[0] * $size[1], 0);\r
+\r
+               $vars = array (\r
+                       'thumbW' => $thumb_size[0],\r
+                       'thumbH' => $thumb_size[1],\r
+                       'reductionW' => $this->smallW, \r
+                       'reductionH' => $smallH,\r
+                       'sizeW' => $size[0],\r
+                       'sizeH' => $size[1],\r
+                       'body' => $body,\r
+                       'thumbUrl' => $this->thumb_collection.'/' . urlencode($this->_getThumbFileName($filename)),\r
+                       'imageUrl' => $this->collection.'/' . urlencode($filename),\r
+                       'mediaUrl' => $CONF['MediaURL'],\r
+                       'fileName' => $filename\r
+               );\r
+               \r
+               // データファイルチェック\r
+               if( ! preg_match("/$this->imgExt/i", $filename) ){\r
+                       $this->_info(__LINE__ . ": 画像ファイルに該当しないので、データファイルテンプレートを使用します");\r
+                       return TEMPLATE :: fill($this->getMemberOption($this->memid, 'dataTpl'), $vars);\r
+               }\r
+\r
+               if ( $thumb_size[0] ) { //サムネイルがある場合のソース\r
+                       $this->_info(__LINE__ . ": サムネイルがあります");\r
+                       return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withThumbTpl'), $vars);\r
+               } else { //サムネイルがない場合のソース\r
+                       if ($size[0] > $this->smallW) { //縮小表示\r
+                               $this->_info(__LINE__ . ": サムネイルがありません、縮小表示します");\r
+                               return TEMPLATE :: fill($this->getMemberOption($this->memid, 'reductionTpl'), $vars);\r
+                       } else { //そのまま表示\r
+                               $this->_info(__LINE__ . ": サムネイルがありません");\r
+                               return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withoutThumbTpl'), $vars);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       function _addDatedItem($blogid, $title, $body, $more, $closed, $timestamp, $future, $catNameOrId = "") {\r
+               // 1. ログイン======================\r
+               $mem = MEMBER :: createFromID($this->memid);\r
+\r
+               // 2. ブログ追加できるかチェック======================\r
+               if (!BLOG :: existsID($this->blogid)) {\r
+                       $this->_info(__LINE__ . ": 存在しないblogです");\r
+                       return false;\r
+               }\r
+               $this->_info(__LINE__ . ": blogidはOK!");\r
+\r
+               if (!$mem->isTeamMember($blogid)) {\r
+                       $this->_warn(__LINE__ . ": メンバーではありません");\r
+                       return false;\r
+               }\r
+               $this->_info(__LINE__ . ": メンバーチェックもok!");\r
+               \r
+               if (!trim($body)) {\r
+                       $this->_warn(__LINE__ . ": 空のアイテムは追加できません");\r
+                       return false;\r
+               }\r
+               $this->_info(__LINE__ . ": アイテムは空じゃないです");\r
+\r
+               // 3. 値の補完\r
+               $blog =& new BLOG($this->blogid);\r
+               if( $blog->isValidCategory($catNameOrId) ){\r
+                       // カテゴリIDとして有効なときはそのまま使う\r
+                       $catid = $catNameOrId;\r
+               } else {\r
+                       // カテゴリID ゲット (誤ったカテゴリID使用時はデフォを使用)\r
+                       $catid = $blog->getCategoryIdFromName($catNameOrId);\r
+               }\r
+               \r
+               $this->_info(__LINE__ . ": 追加するcatid: ".$catid);\r
+               if ($this->DefaultPublish) {\r
+                       $draft = 0;\r
+               } else {\r
+                       $draft = 1; //ドラフト追加\r
+                       $this->_info(__LINE__ . ": ドラフトで追加します");\r
+               }\r
+               if ($closed != 1)\r
+                       $closed = 0; //コメントを許可\r
+               $this->_info(__LINE__ . ": \$catid:".$catid.", \$draft:".$draft.", \$closed:".$closed);\r
+\r
+               // 4. blogに追加\r
+               $itemid = $blog->additem($catid, $title, $body, $more, $blogid, $mem->getID(), $timestamp, $closed, $draft);\r
+               \r
+               $this->_info(__LINE__ . ": itemid: $itemid");\r
+               return $itemid;\r
+       }\r
+       \r
+       function _isSpam($str){\r
+               global $manager;\r
+               if( $this->getOption('spamCheck') == 'yes' ){   \r
+                       $spamcheck = array (\r
+                               'type'          => 'Moblog',\r
+                               'data'          => $str,\r
+                               'return'        => true,\r
+                               'ipblock'   => false\r
+                       );\r
+                       $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));\r
+                       if (isset($spamcheck['result']) && $spamcheck['result'] == true)\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+}\r
index 76dd376..ebd1075 100644 (file)
-<h3>Plugin概要 <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2"><img border="0" src="http://blog.cles.jp/npmoblog.png"  alt="Powered by NP_Moblog" /></a>&nbsp;<a href="http://cles.jp/"><img border="0" src="http://blog.cles.jp/cles.png"  alt="Powered by CLES" /></a></h3>
-
-<p>メールサーバからメールを取得しエントリとして追加するプラグインです。画像ファイル付きのメールにも対応しています。</p>
-
-<h3>必要環境</h3>
-<p>Nucleus: 3.2以降</p>
-<p>PHP: 4.3.x以降</p>
-
-<h3>ファイル構成</h3>
-
-<p>インストール時の構成は以下の通りになります</p>
-
-<p>
-(Nucleusのpluginフォルダ)<br/>
-├NP_Moblog.php<br/>
-│├moblog/<br/>
-│├index.php<br/>
-│└help.html<br/>
-└sharedlibs/<br/>
- └<em>PEAR/共通ライブラリ群</em>
-</p>
-
-<h3>最低限やらなければならないこと</h3>
-
-<p><code>NP_Moblog</code>には開発の過程でたくさんのオプション(設定項目)が追加されてきました。</p>
-<p>これらは全て<code>NP_Moblog</code>を快適に利用するためのものですが、<strong>はじめてお使いになる方はオプションの多さにビックリされている</strong>かもしれません。ここでは<code>NP_Moblog</code>を利用するために最低限やらなければならないことについて解説します。</p>
-
-<p><strong>以下の手順に入る前に、NP_Moblog専用のメールアドレスを準備しておいてください。</strong></p>
-<ol>
-  <li>プラグインをインストールする<em>(このページが確認できれば、おそらくインストールは成功しています)</em></li>
-  <li>「プラグイン管理」からNP_Moblogの「編集」を開く</li>
-  <li>「メール取得の間隔(秒)」を0にする</li>
-  <li>「オプションの保存」をクリックして内容を保存する</li>
-  <li>「スキン編集」ページを開き、自分の使っているスキンへ&lt;%Moblog%&gt;を記入する。</li>
-  <li>「あなたの設定」ページを開く</li>
-  <li>「プラグインを有効にするか?」を<code>はい</code>に変更</li>
-  <li>「POP3 ホスト名」「POP3 ユーザー名」「POP3 パスワード」にメールサーバの情報を設定。(NP_Moblogはここで指定したメールボックスの宛てのメールを取得して追加します。</li>
-  <li>「Nucleusカテゴリ名(Blog名)」に投稿時に使われるカテゴリとblogを指定する。</li>
-  <li>「投稿許可アドレス」にメール送信元のメールアドレスを設定する。</li>
-  <li>「設定の変更」をクリックして記入内容を保存する。</li>
-  <li>メールを送信して、&lt;%Moblog%&gt;を記入したページを表示する。</li>
-</ol>
-
-<p><em>上記で設定しなかったオプションには通常の使用において十分であると考えられる初期値が設定済みになっています。</em></p>
-
-<p>投稿されない場合には「管理操作履歴」を確認してみてください。
-投稿はされるが表示が崩れたりする場合にはテンプレートを調整するとよいでしょう。
-また、投稿が成功したら上記の3で設定した「メール取得の間隔(秒)」を600に戻しておきましょう。</p>
-
-<h3>メールタイトルにオプションを記述する方法</h3>
-
-<p>受信したメールは「メンバーオプション」に設定されている内容にしたがって処理されますが、メールのタイトルにオプションを記述することによってそのメールだけ特別な処理をすることができます。</p>
-
-<p>メールタイトルによって変更可能なオプション</p>
-<ol>
-  <li>「Nucleus blog Id」</li>
-  <li>「Nucleus カテゴリ」</li>
-  <li>「デフォルトで公開するか?」</li>
-</ol>
-
-<p>例えばデフォルトの設定でタイトルを「新規アイテムのタイトル@b=2&amp;c=books&amp;s=1」とした場合、メンバー設定にかかわらず以下のような処理が行われます。</p>
-<ol>
-  <li>「Nucleus blog Id」は2</li>
-  <li>「Nucleus カテゴリ」はbooks</li>
-  <li>「デフォルトで公開するか?」が1 <em>(すぐに公開)</em></li>
-</ol>
-
-<p>カテゴリの名前が間違っているときは、指定されたblogのデフォルトのカテゴリとなります。
-存在しないblogが指定されたときは、追加を行いません。(ログにエラーが記録されます) </p>
-
-<p>オプション記述方法(デフォルト時)</p>
-<ol>
-  <li>オプションの記述開始は半角@</li>
-  <li>値を=で結び、間は&amp;</li>
-  <li>「Nucleus blog Id」のキーは&quot;b&quot;</li>
-  <li>「Nucleus カテゴリ」のキーは&quot;c&quot;</li>
-  <li>「デフォルトで公開するか?」のキーは&quot;s&quot;</li>
-</ol>
-
-<h3>スキンへの記述</h3>
-
-<p>インストールを行っただけではメールの取得は行われません。</p>
-<p>スキン中に以下のタグを記述することにより、メールの取得が行われるようになります。</p>
-
-<table>
-<tr>
-       <th>タグ</th>
-       <th>解説</th>
-</tr>
-<tr>
-       <td>&lt;%Moblog%&gt;</td>
-       <td>このページが表示されるときにメールの取得を行います。<br />
-       前回の取得処理からプラグインオプションで設定してある「メール取得の間隔(秒)」以上の時間が経過していない場合には取得処理を行いません。
-       </td>
-</tr>
-<tr>
-       <td>&lt;%Moblog(link)%&gt;</td>
-       <td>メンバーが<strong>ログインした状態</strong>でこのスキンを表示すると<code>Add Item by Mail</code>というリンクが出現します。これをクリックすることにより、メールの取得処理を行うことができます。<br />
-       これは上記と違い、必ずメール取得処理を行います。
-       </td>
-</tr>
-</table>
-
-<h3>オプション</h3>
-
-<p><code>NP_Moblog</code>には2種類のオプションがあります</p>
-<ol>
-  <li>「プラグイン管理」から設定できる「プラグインオプション」</li>
-  <li>「あなたの設定」や「メンバー管理」からメンバーごとに設定できる「メンバーオプション」</li>
-</ol>
-
-<p>プラグインオプション(サイト全体の設定)</p>
-<table>
-<tr>
-       <th>オプション</th>
-       <th>解説</th>
-</tr>
-<tr>
-       <td>動作モード</td>
-       <td>
-               動作モードを設定します。
-               <ul>
-                 <li>
-                       「互換モード」:通常はこちらを選択してください。
-                 </li>
-                 <li>
-                       「振分対応モード」:<strong>ひとつのメールアカウントを複数のメンバーで共有する</strong>場合にのみこちらを選択してください。<br />
-                       <br />
-                       メンバーオプション内の「投稿を許可するメールアドレス」に記載されていないアドレスから投稿があった場合に<strong>そのメールを削除しないようになります</strong>。
-                       したがって、誰も投稿許可にしていないメールアドレスから投稿があった場合、メールボックスにはどんどんゴミが溜まっていきます。
-                       この場合、<strong>予期せずメールボックスが満杯になる</strong>可能性がありますのでご注意ください。
-                 </li>
-               </ul>
-               デフォルト:「互換モード」   
-       </td>
-</tr>
-<tr>
-       <td>SPAMチェックを有効にする</td>
-       <td>    
-               <code>NP_Blacklist</code>などによりメールのチェックを行います。SPAMと判定された場合にはメールは投稿されません。<br />
-               別途、SPAM判定用のプラグインが必要です。<em>(<code>$manager->notify('SpamCheck',....)</code>に対応したもの)</em>
-       </td>
-</tr>
-<tr>
-       <td>メール取得の間隔(秒)</td>
-       <td>
-               <code>NP_Moblog</code>はスキンに<code>&lt;Moblog&gt;</code>と書かれたページにアクセスがあるとメールを取得するようになっています。しかしながら<code>&lt;Moblog&gt;</code>と書いてあるスキンへのアクセスが多い場合、プラグインはアクセスのたびにメールを取得するので、メールの連続取得による負荷が大きくなる場合があります。そのような状況を回避するためメールの取得処理の間隔を設定することができます。<br />
-               ※テスト時などこの機能を無効化したい場合には0を入力します。<br />
-               デフォルト: 600 (10分)
-       </td>
-</tr>
-<tr>
-       <td>次回更新時刻(変更できません)</td>
-       <td>
-               この時刻以降に<code>&lt;Moblog&gt;</code>と記載されているページにアクセスするとメール取得処理を行う。<br />
-               ※表示用のため変更できません。
-       </td>
-</tr>
-<tr>
-       <td>ログを出力を行うか?</td>
-       <td>
-               <code>はい</code>にすることにより「管理者操作履歴」ログを記録するようになります。
-               膨大なログが出力されるので動作確認時以外は<code>いいえ</code>にしておくことを推奨します。
-               <code>いいえ</code>の場合であっても重大なエラーについては記録されます。<br />
-               デフォルト: いいえ
-       </td>
-</tr>
-</table>
-
-<p>メンバーオプション(メンバーごとの設定)</p>
-<table>
-<tr>
-       <th>オプション</th>
-       <th>解説</th>
-</tr>
-<tr>
-       <td>プラグインを有効にするか?</td>
-       <td>
-               <code>いいえ</code>の場合、このメンバーでメールの取得を行いません。<br />
-               デフォルト: いいえ
-       </td>
-</tr>
-<tr>
-       <td>POP3 ホスト名</td>
-       <td>メールを取得するメールサーバ名</td>
-</tr>
-<tr>
-       <td>POP3 ポート</td>
-       <td>
-               メールを取得するメールサーバのポート。通常は変更しなくて大丈夫です<br />
-               デフォルト: 110
-       </td>
-</tr>
-<tr>
-       <td>POP3 ユーザー名</td>
-       <td>メールを取得するメールサーバのユーザ名</td>
-</tr>
-<tr>
-       <td>POP3 パスワード</td>
-       <td>メールを取得するメールサーバのパスワード</td>
-</tr>
-<tr>
-       <td>APOPを使用するか?</td>
-       <td>
-               メール取得の際にAPOPを利用するかどうかを設定します。<br />
-               デフォルト: いいえ
-       </td>
-</tr>
-<tr>
-       <td>画像を保存するディレクトリ</td>
-       <td>
-               画像を保存するディレクトリを指定します。<br />
-               ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
-       </td>
-</tr>
-<tr>
-       <td>サムネイルを保存するディレクトリ</td>
-       <td>
-               サムネイルを保存するディレクトリを指定します。<br />
-               ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
-       </td>
-</tr>
-<tr>
-       <td>Nucleusカテゴリ(Blog)</td>
-       <td>
-               メールをどのカテゴリ(blog)で投稿するか設定します。
-       </td>
-</tr>
-<tr>
-       <td>イメージ添付メールのみ追加?</td>
-       <td>添付ファイルがある場合のみ、投稿を行います。添付ファイルがないメールは無視されます。</td>
-</tr>
-<tr>
-       <td>デフォルトで公開するか?</td>
-       <td>
-               <code>いいえ</code>の場合には取得したメールをドラフトとして投稿します。投稿内容をドラフトにせずに即時に公開したい場合には設定を<code>はい</code>にします。<br />
-               デフォルト: いいえ      
-       </td>
-</tr>
-<tr>
-       <td>オプション記述開始の区切り文字</td>
-       <td>
-               メールタイトルによってオプションを上書きする場合にオプションの始まりを示す文字。<br />
-               デフォルト: @              
-       </td>
-</tr>
-<tr>
-       <td>オプションでblogidを指定する場合のキー</td>
-       <td>
-               メールタイトルによってオプションを上書きする場合に「Nucleus blog Id」を表すキー<br />
-               デフォルト: b
-       </td>
-</tr>
-<tr>
-       <td>オプションでカテゴリを指定する場合のキー</td>
-       <td>
-               メールタイトルによってオプションを上書きする場合に「Nucleus カテゴリ名/カテゴリID」を表すキー<br />
-               デフォルト: c
-       </td>
-</tr>
-<tr>
-       <td>オプションでストレートにpublish指定する場合のキー</td>
-       <td>
-               メールタイトルによってオプションを上書きする場合に「デフォルトで公開するか?」を表すキー<br />
-               デフォルト: s
-       </td>
-</tr>
-<tr>
-       <td>追記にする場合の区切り文字(利用しない場合は空欄)</td>
-       <td>
-               メールの本文中に指定する文字列があった場合、にその文字列以降の部分が追記になります<br />
-               デフォルト: (空欄)                       
-       </td>
-</tr>
-<tr>
-       <td>投稿許可アドレス(複数の場合改行で区切ってください)</td>
-       <td>
-               投稿を許可するメールの送信元を指定します。ここで指定されない送信元からのメールは追加されません。<br />
-               推奨しませんが*だけを書いた行を追加することで、不特定のユーザから投稿することが可能になります。不用意に使うとblogがSPAMだらけになる可能性がありますので十分注意してください。<br />
-               デフォルト: (空欄)
-       </td>
-</tr>
-<tr>
-       <td>投稿許可SubjectPrefix(制限無しの場合は空欄)</td>
-       <td>
-               Subjectが特定の文字列から始まる場合にのみ投稿が行われます。この機能を利用しないとき空欄にしておいてください。<br />
-               デフォルト: (空欄)
-       </td>
-</tr>
-<tr>
-       <td>件名がないときの題名</td>
-       <td>
-               メールのタイトルがない場合にここで設定した内容をタイトルとして利用します<br />
-               デフォルト: (空欄)
-       </td>
-</tr>
-<tr>
-       <td>htmlメールの場合に除去しないタグ</td>
-       <td>
-               メールをHTML形式で送信した際に、除去しないタグを指定します<br />
-               デフォルト: &lt;title&gt; &lt;hr&gt; &lt;h1&gt; &lt;h2&gt; &lt;h3&gt; &lt;h4&gt; &lt;h5&gt; &lt;h6&gt; &lt;div&gt; &lt;p&gt; &lt;pre&gt; &lt;sup&gt; &lt;ul&gt; &lt;ol&gt; &lt;br&gt; &lt;dl&gt; &lt;dt&gt; &lt;table&gt; &lt;caption&gt; &lt;tr&gt; &lt;li&gt; &lt;dd&gt; &lt;th&gt; &lt;td&gt; &lt;a&gt; &lt;area&gt; &lt;img&gt; &lt;form&gt; &lt;input&gt; &lt;textarea&gt; &lt;button&gt; &lt;select&gt; &lt;option&gt;
-       </td>
-</tr>
-<tr>
-       <td>最大添付量(B)</td>
-       <td>
-               添付ファイルを最大容量を指定します。これ以上の容量のファイルは追加されません。また、添付ファイルの最大容量はこれ以外の設定などによって制限を受ける場合があります<br />
-               デフォルト: 300000
-       </td>
-</tr>
-<tr>
-       <td>対応MIMEタイプ(正規表現)</td>
-       <td>
-               添付ファイルのMIMEタイプがここで指定するパターンに該当する場合にのみ保存を行います。<br />
-               デフォルト: gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf
-       </td>
-</tr>
-<tr>
-       <td>保存しないファイル(正規表現)</td>
-       <td>
-               「対応MIMEタイプ」に該当する場合でも、ファイルがここで指定するパターンに該当する場合には添付ファイルは処理されません。<br />
-               デフォルト: .+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$
-       </td>
-</tr>
-<tr>
-       <td>画像ファイルの拡張子(正規表現)</td>
-       <td>
-               「対応MIMEタイプ」に該当し、ファイルがここで指定するパターンに該当する場合に、添付ファイルが画像ファイルと認識します。それ以外はデータファイルとみなされます。<br />
-               デフォルト: .+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$
-       </td>
-</tr>
-<tr>
-       <td>サムネイルを使用する?</td>
-       <td>
-               サムネイルを自動的に作成するか指定します<br />
-               デフォルト: はい
-       </td>
-</tr>
-<tr>
-       <td>サムネイルの大きさ(Width)</td>
-       <td>
-               生成されるサムネイルの最大横幅を指定します<br />
-               デフォルト: 120
-       </td>
-</tr>
-<tr>
-       <td>サムネイルの大きさ(Hight)</td>
-       <td>
-               生成されるサムネイルの最大縦幅を指定します<br />
-               デフォルト: 120
-       </td>
-</tr>
-<tr>
-       <td>サムネイルを作る対象画像</td>
-       <td>
-               サムネイル生成の対象となるファイル名のパターンを指定します<br />
-               デフォルト: .+\.jpe?g$|.+\.png$
-       </td>
-</tr>
-<tr>
-       <td>アイテム内に表示する画像の最大横幅</td>
-       <td>
-               サムネイルを生成しない場合に、IMGタグに挿入される横幅を指定します<br />
-               デフォルト: 120
-       </td>
-</tr>
-<tr>
-       <td>テキストテンプレート</td>
-       <td>
-               添付ファイルがない場合に使われるテンプレートです<br />
-               テンプレート内で使えるタグについては別途解説してあります
-       </td>
-</tr>
-<tr>
-       <td>サムネイル付きテンプレート</td>
-       <td>
-               サムネイルを使うように設定してあり、なおかつサムネイルがきちんと生成できた場合に使われるテンプレートです<br />
-               テンプレート内で使えるタグについては別途解説してあります
-       </td>
-</tr>
-<tr>
-       <td>サムネイルなしテンプレート</td>
-       <td>
-               サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超えない場合、もしくは「対応MIMEタイプ」に含まれるが「サムネイルを作る対象画像」に該当する場合に使われるテンプレートです。<br />
-               テンプレート内で使えるタグについては別途解説してあります
-       </td>
-</tr>
-<tr>
-       <td>サムネイルなしテンプレート(縮小)</td>
-       <td>
-               サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超える場合に使われるテンプレートです<br />
-               テンプレート内で使えるタグについては別途解説してあります
-       </td>
-</tr>
-<tr>
-       <td>データファイルテンプレート</td>
-       <td>
-               画像以外の添付ファイルがあるときに使われるテンプレートです<br />
-               テンプレート内で使えるタグについては別途解説してあります
-       </td>
-</tr>
-</table>
-
-<h3>テンプレート内で利用可能なタグ</h3>
-
-<p>テンプレート中には以下のタグが利用できます。</p>
-<p>(テンプレートによっては使えないものもあります)</p>
-
-<table>
-<tr>
-       <th>タグ</th>
-       <th>解説</th>
-</tr>
-<tr>
-       <td>&lt;%sizeW%&gt;</td>
-       <td>オリジナル画像の横幅</td>
-</tr>
-<tr>
-       <td>&lt;%sizeH%&gt;</td>
-       <td>オリジナル画像の縦幅</td>
-</tr>
-<tr>
-       <td>&lt;%thumbW%&gt;</td>
-       <td>生成されたサムネイルの横幅</td>
-</tr>
-<tr>
-       <td>&lt;%thumbH%&gt;</td>
-       <td>生成されたサムネイルの縦幅</td>
-</tr>
-<tr>
-       <td>&lt;%reductionW%&gt;</td>
-       <td>画像を縮小表示するときの横幅</td>
-</tr>
-<tr>
-       <td>&lt;%reductionH%&gt;</td>
-       <td>画像を縮小表示するときの縦幅</td>
-</tr>
-<tr>
-       <td>&lt;%thumbUrl%&gt;</td>
-       <td>生成されたサムネイルのURL</td>
-</tr>
-<tr>
-       <td>&lt;%imageUrl%&gt;</td>
-       <td>オリジナル画像(添付ファイル)のURL</td>
-</tr>
-<tr>
-       <td>&lt;%mediaUrl%&gt;</td>
-       <td>mediaディレクトリのURL</td>
-</tr>
-<tr>
-       <td>&lt;%fileName%&gt;</td>
-       <td>添付ファイルのファイル名</td>
-</tr>
-<tr>
-       <td>&lt;%body%&gt;</td>
-       <td>メールの本文</td>
-</tr>
-</table>
-
-<h3>サポートとバグレポート</h3>
-
-<p>問題が解決できない場合には<a href="http://japan.nucleuscms.org/bb/">Nucleus(JP)フォーラム</a>を活用しましょう。</p>
-<p>バグレポートについては配布元の<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>にて受け付けていますので、該当のバージョンのエントリにコメント又はトラックバックでどうぞ。</p>
-<p>「動作確認しました」というだけでも開発者には重要な情報になります。</p>
-
-<h3>アンインストール</h3>
-
-<p><code>NP_Moblog</code>を完全にアンインストールするための手順は以下の通りです</p>
-
-<ol>
-  <li>「プラグイン管理」からNP_Moblogをアンインストールする</li>
-  <li>Nucleusをインストールしているサーバの<code>nucleus/plugin/</code>ディレクトリから<code>NP_Moblog.php</code>と<code>moblog</code>ディレクトリを削除する</li>
-</ol>
-
-<h3>バージョン履歴</h3>
-
-<p>新バージョンは<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>で確認してください。</p>
-
-<ul>
-       <li>Version 1.16: (2007/03/17)</li>
-       <li> [Fixed] メールアドレスの大文字小文字を区別しないようにした</li>
-       <li> [Fixed] 管理者が設定の変更を行った場合、useridディレクトリにきちんと画像が保存されない問題を修正</li>
-       <li>Version 1.15: (2006/09/30)</li>
-       <li> [Fixed] セキュリティの向上</li>
-       <li>Version 1.14: (2006/07/15)</li>
-       <li> [Added] 画像・サムネイルの保存先を指定できるようにした</li>
-       <li> [Fixed] ファイル名のPrefixをuniqid()からYYYYmmddHHiissnn形式(日付+連番)に変更した</li>
-       <li> [Fixed] ユーザーがSuper-Adminである場合に任意のBlog、カテゴリで投稿できる問題を修正</li>
-       <li> [Fixed] ブログ名、カテゴリ名に特定の文字が入っている場合に、投稿が出来ない問題を修正</li>
-       <li>Version 1.13: (2006/03/24)</li>
-       <li> [Fixed] HTMLメールが上手く扱えない問題を修正</li>
-       <li>Version 1.12: (2005/03/19)</li>
-       <li> [Fixed] ライセンスを変更した</li>
-       <li>Version 1.11: (2005/12/30)</li>
-       <li> [Fixed] マニュアルの誤記、XHTMLに準拠していなかった部分を修正</li>
-       <li> [Fixed] PEARファイル群のinclude()の方法を改善</li>
-       <li>Version 1.10: (2005/09/01)</li>
-       <li> [Fixed] 「イメージ添付メールのみ追加?」が「はい」の場合に、添付ファイルが存在しているにもかかわらず、エントリが追加されない不具合を修正</li>
-       <li> [Added] 管理画面から動作確認ができる機能を追加</li>
-       <li>Version 1.9: (2005/09/01)</li>
-       <li> [Changed] 「Nucleus blog Id」「Nucleus カテゴリ名/カテゴリID」を「Nucleusカテゴリ名(Blog名)」変更</li>
-       <li>Version 1.8: (2005/07/25)</li>
-       <li> [Fixed] PEARクラスのinclude()方法を変更した</li>
-       <li>Version 1.7: (2005/07/07)</li>
-       <li> [Fixed] 1.6で修正されていなかったオプション分割の不具合を修正</li>
-       <li>Version 1.6: (2005/07/02)</li>
-       <li> [Added] テキストテンプレート(添付ファイルがない場合のテンプレート)を追加</li>
-       <li> [Added] SPAM Checkに対応(別途、NP_BlackListなどが必要です)</li>
-       <li> [Added] Help(このファイル)を同梱</li>
-       <li> [Fixed] オプション分割の不具合を修正</li>
-       <li> [Fixed] Nucleus v3.2以前のバージョンにインストールできないようにした。(getMinNucleusVersionを設定)</li>
-</ul>
-
-<h3>Special Thanks</h3>
-
-<p>NP_Moblogははまみおさんの<a href="http://nakahara21.com/?itemid=133">NP_HeelloWorld v0.8</a>を基に開発しました。原作者のまみおさんに感謝します。</p>
-
-<h3>開発者について</h3>
-
-<ul>
-  <li><a href="http://blog.cles.jp/member/1">hsur</a> (<a href="http://blog.cles.jp/">cles::blog</a>)</li>
-  <li>配布元: <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2">NP_cles() - NP_Moblog</a></li>
-  <li><a href="http://blog.cles.jp/np_cles/"><img border="0" src="http://blog.cles.jp/npmoblog.png"  alt="Powered by NP_Moblog" /></a> ← ご自由にご利用ください。</li>
-  <li>ドネーションや仕事のご依頼も歓迎します。</li>
-</ul>
\ No newline at end of file
+<h3>Plugin概要 <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2"><img border="0" src="http://blog.cles.jp/npmoblog.png"  alt="Powered by NP_Moblog" /></a>&nbsp;<a href="http://cles.jp/"><img border="0" src="http://blog.cles.jp/cles.png"  alt="Powered by CLES" /></a></h3>\r
+\r
+<p>メールサーバからメールを取得しエントリとして追加するプラグインです。画像ファイル付きのメールにも対応しています。</p>\r
+\r
+<h3>必要環境</h3>\r
+<p>Nucleus: 3.2以降</p>\r
+<p>PHP: 4.3.x以降</p>\r
+\r
+<h3>ファイル構成</h3>\r
+\r
+<p>インストール時の構成は以下の通りになります</p>\r
+\r
+<p>\r
+(Nucleusのpluginフォルダ)<br/>\r
+├NP_Moblog.php<br/>\r
+│├moblog/<br/>\r
+│├index.php<br/>\r
+│└help.html<br/>\r
+└sharedlibs/<br/>\r
+ └<em>PEAR/共通ライブラリ群</em>\r
+</p>\r
+\r
+<h3>最低限やらなければならないこと</h3>\r
+\r
+<p><code>NP_Moblog</code>には開発の過程でたくさんのオプション(設定項目)が追加されてきました。</p>\r
+<p>これらは全て<code>NP_Moblog</code>を快適に利用するためのものですが、<strong>はじめてお使いになる方はオプションの多さにビックリされている</strong>かもしれません。ここでは<code>NP_Moblog</code>を利用するために最低限やらなければならないことについて解説します。</p>\r
+\r
+<p><strong>以下の手順に入る前に、NP_Moblog専用のメールアドレスを準備しておいてください。</strong></p>\r
+<ol>\r
+  <li>プラグインをインストールする<em>(このページが確認できれば、おそらくインストールは成功しています)</em></li>\r
+  <li>「プラグイン管理」からNP_Moblogの「編集」を開く</li>\r
+  <li>「メール取得の間隔(秒)」を0にする</li>\r
+  <li>「オプションの保存」をクリックして内容を保存する</li>\r
+  <li>「スキン編集」ページを開き、自分の使っているスキンへ&lt;%Moblog%&gt;を記入する。</li>\r
+  <li>「あなたの設定」ページを開く</li>\r
+  <li>「プラグインを有効にするか?」を<code>はい</code>に変更</li>\r
+  <li>「POP3 ホスト名」「POP3 ユーザー名」「POP3 パスワード」にメールサーバの情報を設定。(NP_Moblogはここで指定したメールボックスの宛てのメールを取得して追加します。</li>\r
+  <li>「Nucleusカテゴリ名(Blog名)」に投稿時に使われるカテゴリとblogを指定する。</li>\r
+  <li>「投稿許可アドレス」にメール送信元のメールアドレスを設定する。</li>\r
+  <li>「設定の変更」をクリックして記入内容を保存する。</li>\r
+  <li>メールを送信して、&lt;%Moblog%&gt;を記入したページを表示する。</li>\r
+</ol>\r
+\r
+<p><em>上記で設定しなかったオプションには通常の使用において十分であると考えられる初期値が設定済みになっています。</em></p>\r
+\r
+<p>投稿されない場合には「管理操作履歴」を確認してみてください。\r
+投稿はされるが表示が崩れたりする場合にはテンプレートを調整するとよいでしょう。\r
+また、投稿が成功したら上記の3で設定した「メール取得の間隔(秒)」を600に戻しておきましょう。</p>\r
+\r
+<h3>メールタイトルにオプションを記述する方法</h3>\r
+\r
+<p>受信したメールは「メンバーオプション」に設定されている内容にしたがって処理されますが、メールのタイトルにオプションを記述することによってそのメールだけ特別な処理をすることができます。</p>\r
+\r
+<p>メールタイトルによって変更可能なオプション</p>\r
+<ol>\r
+  <li>「Nucleus blog Id」</li>\r
+  <li>「Nucleus カテゴリ」</li>\r
+  <li>「デフォルトで公開するか?」</li>\r
+</ol>\r
+\r
+<p>例えばデフォルトの設定でタイトルを「新規アイテムのタイトル@b=2&amp;c=books&amp;s=1」とした場合、メンバー設定にかかわらず以下のような処理が行われます。</p>\r
+<ol>\r
+  <li>「Nucleus blog Id」は2</li>\r
+  <li>「Nucleus カテゴリ」はbooks</li>\r
+  <li>「デフォルトで公開するか?」が1 <em>(すぐに公開)</em></li>\r
+</ol>\r
+\r
+<p>カテゴリの名前が間違っているときは、指定されたblogのデフォルトのカテゴリとなります。\r
+存在しないblogが指定されたときは、追加を行いません。(ログにエラーが記録されます) </p>\r
+\r
+<p>オプション記述方法(デフォルト時)</p>\r
+<ol>\r
+  <li>オプションの記述開始は半角@</li>\r
+  <li>値を=で結び、間は&amp;</li>\r
+  <li>「Nucleus blog Id」のキーは&quot;b&quot;</li>\r
+  <li>「Nucleus カテゴリ」のキーは&quot;c&quot;</li>\r
+  <li>「デフォルトで公開するか?」のキーは&quot;s&quot;</li>\r
+</ol>\r
+\r
+<h3>スキンへの記述</h3>\r
+\r
+<p>インストールを行っただけではメールの取得は行われません。</p>\r
+<p>スキン中に以下のタグを記述することにより、メールの取得が行われるようになります。</p>\r
+\r
+<table>\r
+<tr>\r
+       <th>タグ</th>\r
+       <th>解説</th>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%Moblog%&gt;</td>\r
+       <td>このページが表示されるときにメールの取得を行います。<br />\r
+       前回の取得処理からプラグインオプションで設定してある「メール取得の間隔(秒)」以上の時間が経過していない場合には取得処理を行いません。\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%Moblog(link)%&gt;</td>\r
+       <td>メンバーが<strong>ログインした状態</strong>でこのスキンを表示すると<code>Add Item by Mail</code>というリンクが出現します。これをクリックすることにより、メールの取得処理を行うことができます。<br />\r
+       これは上記と違い、必ずメール取得処理を行います。\r
+       </td>\r
+</tr>\r
+</table>\r
+\r
+<h3>オプション</h3>\r
+\r
+<p><code>NP_Moblog</code>には2種類のオプションがあります</p>\r
+<ol>\r
+  <li>「プラグイン管理」から設定できる「プラグインオプション」</li>\r
+  <li>「あなたの設定」や「メンバー管理」からメンバーごとに設定できる「メンバーオプション」</li>\r
+</ol>\r
+\r
+<p>プラグインオプション(サイト全体の設定)</p>\r
+<table>\r
+<tr>\r
+       <th>オプション</th>\r
+       <th>解説</th>\r
+</tr>\r
+<tr>\r
+       <td>動作モード</td>\r
+       <td>\r
+               動作モードを設定します。\r
+               <ul>\r
+                 <li>\r
+                       「互換モード」:通常はこちらを選択してください。\r
+                 </li>\r
+                 <li>\r
+                       「振分対応モード」:<strong>ひとつのメールアカウントを複数のメンバーで共有する</strong>場合にのみこちらを選択してください。<br />\r
+                       <br />\r
+                       メンバーオプション内の「投稿を許可するメールアドレス」に記載されていないアドレスから投稿があった場合に<strong>そのメールを削除しないようになります</strong>。\r
+                       したがって、誰も投稿許可にしていないメールアドレスから投稿があった場合、メールボックスにはどんどんゴミが溜まっていきます。\r
+                       この場合、<strong>予期せずメールボックスが満杯になる</strong>可能性がありますのでご注意ください。\r
+                 </li>\r
+               </ul>\r
+               デフォルト:「互換モード」   \r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>SPAMチェックを有効にする</td>\r
+       <td>    \r
+               <code>NP_Blacklist</code>などによりメールのチェックを行います。SPAMと判定された場合にはメールは投稿されません。<br />\r
+               別途、SPAM判定用のプラグインが必要です。<em>(<code>$manager->notify('SpamCheck',....)</code>に対応したもの)</em>\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>メール取得の間隔(秒)</td>\r
+       <td>\r
+               <code>NP_Moblog</code>はスキンに<code>&lt;Moblog&gt;</code>と書かれたページにアクセスがあるとメールを取得するようになっています。しかしながら<code>&lt;Moblog&gt;</code>と書いてあるスキンへのアクセスが多い場合、プラグインはアクセスのたびにメールを取得するので、メールの連続取得による負荷が大きくなる場合があります。そのような状況を回避するためメールの取得処理の間隔を設定することができます。<br />\r
+               ※テスト時などこの機能を無効化したい場合には0を入力します。<br />\r
+               デフォルト: 600 (10分)\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>次回更新時刻(変更できません)</td>\r
+       <td>\r
+               この時刻以降に<code>&lt;Moblog&gt;</code>と記載されているページにアクセスするとメール取得処理を行う。<br />\r
+               ※表示用のため変更できません。\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>ログを出力を行うか?</td>\r
+       <td>\r
+               <code>はい</code>にすることにより「管理者操作履歴」ログを記録するようになります。\r
+               膨大なログが出力されるので動作確認時以外は<code>いいえ</code>にしておくことを推奨します。\r
+               <code>いいえ</code>の場合であっても重大なエラーについては記録されます。<br />\r
+               デフォルト: いいえ\r
+       </td>\r
+</tr>\r
+</table>\r
+\r
+<p>メンバーオプション(メンバーごとの設定)</p>\r
+<table>\r
+<tr>\r
+       <th>オプション</th>\r
+       <th>解説</th>\r
+</tr>\r
+<tr>\r
+       <td>プラグインを有効にするか?</td>\r
+       <td>\r
+               <code>いいえ</code>の場合、このメンバーでメールの取得を行いません。<br />\r
+               デフォルト: いいえ\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>POP3 ホスト名</td>\r
+       <td>メールを取得するメールサーバ名</td>\r
+</tr>\r
+<tr>\r
+       <td>POP3 ポート</td>\r
+       <td>\r
+               メールを取得するメールサーバのポート。通常は変更しなくて大丈夫です<br />\r
+               デフォルト: 110\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>POP3 ユーザー名</td>\r
+       <td>メールを取得するメールサーバのユーザ名</td>\r
+</tr>\r
+<tr>\r
+       <td>POP3 パスワード</td>\r
+       <td>メールを取得するメールサーバのパスワード</td>\r
+</tr>\r
+<tr>\r
+       <td>APOPを使用するか?</td>\r
+       <td>\r
+               メール取得の際にAPOPを利用するかどうかを設定します。<br />\r
+               デフォルト: いいえ\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>画像を保存するディレクトリ</td>\r
+       <td>\r
+               画像を保存するディレクトリを指定します。<br />\r
+               ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルを保存するディレクトリ</td>\r
+       <td>\r
+               サムネイルを保存するディレクトリを指定します。<br />\r
+               ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>Nucleusカテゴリ(Blog)</td>\r
+       <td>\r
+               メールをどのカテゴリ(blog)で投稿するか設定します。\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>イメージ添付メールのみ追加?</td>\r
+       <td>添付ファイルがある場合のみ、投稿を行います。添付ファイルがないメールは無視されます。</td>\r
+</tr>\r
+<tr>\r
+       <td>デフォルトで公開するか?</td>\r
+       <td>\r
+               <code>いいえ</code>の場合には取得したメールをドラフトとして投稿します。投稿内容をドラフトにせずに即時に公開したい場合には設定を<code>はい</code>にします。<br />\r
+               デフォルト: いいえ      \r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>オプション記述開始の区切り文字</td>\r
+       <td>\r
+               メールタイトルによってオプションを上書きする場合にオプションの始まりを示す文字。<br />\r
+               デフォルト: @              \r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>オプションでblogidを指定する場合のキー</td>\r
+       <td>\r
+               メールタイトルによってオプションを上書きする場合に「Nucleus blog Id」を表すキー<br />\r
+               デフォルト: b\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>オプションでカテゴリを指定する場合のキー</td>\r
+       <td>\r
+               メールタイトルによってオプションを上書きする場合に「Nucleus カテゴリ名/カテゴリID」を表すキー<br />\r
+               デフォルト: c\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>オプションでストレートにpublish指定する場合のキー</td>\r
+       <td>\r
+               メールタイトルによってオプションを上書きする場合に「デフォルトで公開するか?」を表すキー<br />\r
+               デフォルト: s\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>追記にする場合の区切り文字(利用しない場合は空欄)</td>\r
+       <td>\r
+               メールの本文中に指定する文字列があった場合、にその文字列以降の部分が追記になります<br />\r
+               デフォルト: (空欄)                       \r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>投稿許可アドレス(複数の場合改行で区切ってください)</td>\r
+       <td>\r
+               投稿を許可するメールの送信元を指定します。ここで指定されない送信元からのメールは追加されません。<br />\r
+               推奨しませんが*だけを書いた行を追加することで、不特定のユーザから投稿することが可能になります。不用意に使うとblogがSPAMだらけになる可能性がありますので十分注意してください。<br />\r
+               デフォルト: (空欄)\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>投稿許可SubjectPrefix(制限無しの場合は空欄)</td>\r
+       <td>\r
+               Subjectが特定の文字列から始まる場合にのみ投稿が行われます。この機能を利用しないとき空欄にしておいてください。<br />\r
+               デフォルト: (空欄)\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>件名がないときの題名</td>\r
+       <td>\r
+               メールのタイトルがない場合にここで設定した内容をタイトルとして利用します<br />\r
+               デフォルト: (空欄)\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>htmlメールの場合に除去しないタグ</td>\r
+       <td>\r
+               メールをHTML形式で送信した際に、除去しないタグを指定します<br />\r
+               デフォルト: &lt;title&gt; &lt;hr&gt; &lt;h1&gt; &lt;h2&gt; &lt;h3&gt; &lt;h4&gt; &lt;h5&gt; &lt;h6&gt; &lt;div&gt; &lt;p&gt; &lt;pre&gt; &lt;sup&gt; &lt;ul&gt; &lt;ol&gt; &lt;br&gt; &lt;dl&gt; &lt;dt&gt; &lt;table&gt; &lt;caption&gt; &lt;tr&gt; &lt;li&gt; &lt;dd&gt; &lt;th&gt; &lt;td&gt; &lt;a&gt; &lt;area&gt; &lt;img&gt; &lt;form&gt; &lt;input&gt; &lt;textarea&gt; &lt;button&gt; &lt;select&gt; &lt;option&gt;\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>最大添付量(B)</td>\r
+       <td>\r
+               添付ファイルを最大容量を指定します。これ以上の容量のファイルは追加されません。また、添付ファイルの最大容量はこれ以外の設定などによって制限を受ける場合があります<br />\r
+               デフォルト: 300000\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>対応MIMEタイプ(正規表現)</td>\r
+       <td>\r
+               添付ファイルのMIMEタイプがここで指定するパターンに該当する場合にのみ保存を行います。<br />\r
+               デフォルト: gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>保存しないファイル(正規表現)</td>\r
+       <td>\r
+               「対応MIMEタイプ」に該当する場合でも、ファイルがここで指定するパターンに該当する場合には添付ファイルは処理されません。<br />\r
+               デフォルト: .+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>画像ファイルの拡張子(正規表現)</td>\r
+       <td>\r
+               「対応MIMEタイプ」に該当し、ファイルがここで指定するパターンに該当する場合に、添付ファイルが画像ファイルと認識します。それ以外はデータファイルとみなされます。<br />\r
+               デフォルト: .+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルを使用する?</td>\r
+       <td>\r
+               サムネイルを自動的に作成するか指定します<br />\r
+               デフォルト: はい\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルの大きさ(Width)</td>\r
+       <td>\r
+               生成されるサムネイルの最大横幅を指定します<br />\r
+               デフォルト: 120\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルの大きさ(Hight)</td>\r
+       <td>\r
+               生成されるサムネイルの最大縦幅を指定します<br />\r
+               デフォルト: 120\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルを作る対象画像</td>\r
+       <td>\r
+               サムネイル生成の対象となるファイル名のパターンを指定します<br />\r
+               デフォルト: .+\.jpe?g$|.+\.png$\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>アイテム内に表示する画像の最大横幅</td>\r
+       <td>\r
+               サムネイルを生成しない場合に、IMGタグに挿入される横幅を指定します<br />\r
+               デフォルト: 120\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>テキストテンプレート</td>\r
+       <td>\r
+               添付ファイルがない場合に使われるテンプレートです<br />\r
+               テンプレート内で使えるタグについては別途解説してあります\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイル付きテンプレート</td>\r
+       <td>\r
+               サムネイルを使うように設定してあり、なおかつサムネイルがきちんと生成できた場合に使われるテンプレートです<br />\r
+               テンプレート内で使えるタグについては別途解説してあります\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルなしテンプレート</td>\r
+       <td>\r
+               サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超えない場合、もしくは「対応MIMEタイプ」に含まれるが「サムネイルを作る対象画像」に該当する場合に使われるテンプレートです。<br />\r
+               テンプレート内で使えるタグについては別途解説してあります\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>サムネイルなしテンプレート(縮小)</td>\r
+       <td>\r
+               サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超える場合に使われるテンプレートです<br />\r
+               テンプレート内で使えるタグについては別途解説してあります\r
+       </td>\r
+</tr>\r
+<tr>\r
+       <td>データファイルテンプレート</td>\r
+       <td>\r
+               画像以外の添付ファイルがあるときに使われるテンプレートです<br />\r
+               テンプレート内で使えるタグについては別途解説してあります\r
+       </td>\r
+</tr>\r
+</table>\r
+\r
+<h3>テンプレート内で利用可能なタグ</h3>\r
+\r
+<p>テンプレート中には以下のタグが利用できます。</p>\r
+<p>(テンプレートによっては使えないものもあります)</p>\r
+\r
+<table>\r
+<tr>\r
+       <th>タグ</th>\r
+       <th>解説</th>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%sizeW%&gt;</td>\r
+       <td>オリジナル画像の横幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%sizeH%&gt;</td>\r
+       <td>オリジナル画像の縦幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%thumbW%&gt;</td>\r
+       <td>生成されたサムネイルの横幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%thumbH%&gt;</td>\r
+       <td>生成されたサムネイルの縦幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%reductionW%&gt;</td>\r
+       <td>画像を縮小表示するときの横幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%reductionH%&gt;</td>\r
+       <td>画像を縮小表示するときの縦幅</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%thumbUrl%&gt;</td>\r
+       <td>生成されたサムネイルのURL</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%imageUrl%&gt;</td>\r
+       <td>オリジナル画像(添付ファイル)のURL</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%mediaUrl%&gt;</td>\r
+       <td>mediaディレクトリのURL</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%fileName%&gt;</td>\r
+       <td>添付ファイルのファイル名</td>\r
+</tr>\r
+<tr>\r
+       <td>&lt;%body%&gt;</td>\r
+       <td>メールの本文</td>\r
+</tr>\r
+</table>\r
+\r
+<h3>サポートとバグレポート</h3>\r
+\r
+<p>問題が解決できない場合には<a href="http://japan.nucleuscms.org/bb/">Nucleus(JP)フォーラム</a>を活用しましょう。</p>\r
+<p>バグレポートについては配布元の<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>にて受け付けていますので、該当のバージョンのエントリにコメント又はトラックバックでどうぞ。</p>\r
+<p>「動作確認しました」というだけでも開発者には重要な情報になります。</p>\r
+\r
+<h3>アンインストール</h3>\r
+\r
+<p><code>NP_Moblog</code>を完全にアンインストールするための手順は以下の通りです</p>\r
+\r
+<ol>\r
+  <li>「プラグイン管理」からNP_Moblogをアンインストールする</li>\r
+  <li>Nucleusをインストールしているサーバの<code>nucleus/plugin/</code>ディレクトリから<code>NP_Moblog.php</code>と<code>moblog</code>ディレクトリを削除する</li>\r
+</ol>\r
+\r
+<h3>バージョン履歴</h3>\r
+\r
+<p>新バージョンは<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>で確認してください。</p>\r
+\r
+<ul>\r
+       <!--  \r
+       <li> //TODO: [Fixed] 日本語のファイル添付に対応 </li>\r
+       -->\r
+\r
+       <li>Version 1.17: (2010/06/06)</li>\r
+       <li> [Fixed] エンコーディングが適切に検出されない問題を修正</li>\r
+       <li> [Fixed] mysql_query()をsql_query()に変更</li>\r
+       <li> [Fixed] eregi()をpreg_match()に変更</li>\r
+       <li> [Fixed] register_globals,allow_url_fopen,allow_url_includeがonの場合にリモートコードインジェクションが発生する問題に対応しました (Thanks Katsumiさん)</li>\r
+       <li>Version 1.16: (2007/03/17)</li>\r
+       <li> [Fixed] メールアドレスの大文字小文字を区別しないようにした</li>\r
+       <li> [Fixed] 管理者が設定の変更を行った場合、useridディレクトリにきちんと画像が保存されない問題を修正</li>\r
+       <li>Version 1.15: (2006/09/30)</li>\r
+       <li> [Fixed] セキュリティの向上</li>\r
+       <li>Version 1.14: (2006/07/15)</li>\r
+       <li> [Added] 画像・サムネイルの保存先を指定できるようにした</li>\r
+       <li> [Fixed] ファイル名のPrefixをuniqid()からYYYYmmddHHiissnn形式(日付+連番)に変更した</li>\r
+       <li> [Fixed] ユーザーがSuper-Adminである場合に任意のBlog、カテゴリで投稿できる問題を修正</li>\r
+       <li> [Fixed] ブログ名、カテゴリ名に特定の文字が入っている場合に、投稿が出来ない問題を修正</li>\r
+       <li>Version 1.13: (2006/03/24)</li>\r
+       <li> [Fixed] HTMLメールが上手く扱えない問題を修正</li>\r
+       <li>Version 1.12: (2005/03/19)</li>\r
+       <li> [Fixed] ライセンスを変更した</li>\r
+       <li>Version 1.11: (2005/12/30)</li>\r
+       <li> [Fixed] マニュアルの誤記、XHTMLに準拠していなかった部分を修正</li>\r
+       <li> [Fixed] PEARファイル群のinclude()の方法を改善</li>\r
+       <li>Version 1.10: (2005/09/01)</li>\r
+       <li> [Fixed] 「イメージ添付メールのみ追加?」が「はい」の場合に、添付ファイルが存在しているにもかかわらず、エントリが追加されない不具合を修正</li>\r
+       <li> [Added] 管理画面から動作確認ができる機能を追加</li>\r
+       <li>Version 1.9: (2005/09/01)</li>\r
+       <li> [Changed] 「Nucleus blog Id」「Nucleus カテゴリ名/カテゴリID」を「Nucleusカテゴリ名(Blog名)」変更</li>\r
+       <li>Version 1.8: (2005/07/25)</li>\r
+       <li> [Fixed] PEARクラスのinclude()方法を変更した</li>\r
+       <li>Version 1.7: (2005/07/07)</li>\r
+       <li> [Fixed] 1.6で修正されていなかったオプション分割の不具合を修正</li>\r
+       <li>Version 1.6: (2005/07/02)</li>\r
+       <li> [Added] テキストテンプレート(添付ファイルがない場合のテンプレート)を追加</li>\r
+       <li> [Added] SPAM Checkに対応(別途、NP_BlackListなどが必要です)</li>\r
+       <li> [Added] Help(このファイル)を同梱</li>\r
+       <li> [Fixed] オプション分割の不具合を修正</li>\r
+       <li> [Fixed] Nucleus v3.2以前のバージョンにインストールできないようにした。(getMinNucleusVersionを設定)</li>\r
+</ul>\r
+\r
+<h3>Special Thanks</h3>\r
+\r
+<p>NP_Moblogははまみおさんの<a href="http://nakahara21.com/?itemid=133">NP_HeelloWorld v0.8</a>を基に開発しました。原作者のまみおさんに感謝します。</p>\r
+\r
+<h3>開発者について</h3>\r
+\r
+<ul>\r
+  <li><a href="http://blog.cles.jp/member/1">hsur</a> (<a href="http://blog.cles.jp/">cles::blog</a>)</li>\r
+  <li>配布元: <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2">NP_cles() - NP_Moblog</a></li>\r
+  <li><a href="http://blog.cles.jp/np_cles/"><img border="0" src="http://blog.cles.jp/npmoblog.png"  alt="Powered by NP_Moblog" /></a> ← ご自由にご利用ください。</li>\r
+  <li>ドネーションや仕事のご依頼も歓迎します。</li>\r
+</ul>\r
index 364ce7f..c2638ac 100644 (file)
-<?php
-// vim: tabstop=2:shiftwidth=2
-
-/**
-  * index.php ($Revision: 1.1 $)
-  * 
-  * by hsur ( http://blog.cles.jp/np_cles )
-  * $Id: index.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-*/
-
-/*
-  * Copyright (C) 2004-2006 cles. All rights reserved.
-  *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * as published by the Free Software Foundation; either version 2
-  * of the License, or (at your option) any later version.
-  * 
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  * 
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-  * 
-  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
-  * permission to link the code of this program with those files in the PEAR
-  * library that are licensed under the PHP License (or with modified versions
-  * of those files that use the same license as those files), and distribute
-  * linked combinations including the two. You must obey the GNU General Public
-  * License in all respects for all of the code used other than those files in
-  * the PEAR library that are licensed under the PHP License. If you modify
-  * this file, you may extend this exception to your version of the file,
-  * but you are not obligated to do so. If you do not wish to do so, delete
-  * this exception statement from your version.
-*/
-
-$strRel = '../../../';
-include ($strRel.'config.php');
-include ($DIR_LIBS.'PLUGINADMIN.php');
-
-require_once($DIR_PLUGINS . 'sharedlibs/sharedlibs.php');
-require_once('cles/Feedback.php');
-
-$required = '4.3.0';
-if( ! version_compare(phpversion() , $required , '>=') ){
-       ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
-}
-
-if ($blogid) {
-       $isblogadmin = $member->isBlogAdmin($blogid);
-} else
-       $isblogadmin = 0;
-
-if (!$member->isLoggedIn()) {
-       $oPluginAdmin = new PluginAdmin('Moblog');
-       $oPluginAdmin->start();
-       echo "<p>ログインが必要です</p>";
-       $oPluginAdmin->end();
-       exit;
-}
-
-if (!($member->isAdmin() || $isblogadmin)) {
-       $oPluginAdmin = new PluginAdmin('Moblog');
-       $oPluginAdmin->start();
-       echo "<p>"._ERROR_DISALLOWED."</p>";
-       $oPluginAdmin->end();
-       exit;
-}
-
-if (isset ($_GET['page'])) {
-       $action = getVar('page');
-}
-if (isset ($_POST['page'])) {
-       $action = getVar('page');
-}
-
-// create the admin area page
-$oPluginAdmin = new PluginAdmin('Moblog');
-$oPluginAdmin->start();
-$fb =& new cles_Feedback($oPluginAdmin);
-
-// menu
-echo "<h2>Moblog menu</h2>\n";
-echo "<ul>\n";
-echo "<li><a href=\"".serverVar('PHP_SELF')."?page=report\"><span style=\"font-weight:bold; color:red\">" . $fb->getMenuStr() . "</span></a></li>\n";
-echo "</ul>\n";
-
-//action
-switch ($action) {
-       case 'report' :
-               $fb->printForm();
-               break;
-
-       default :
-               break;
-}
-
-echo "<br />";
-
-$oPluginAdmin->end();
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+/**\r
+  * index.php ($Revision: 1.21 $)\r
+  * \r
+  * by hsur ( http://blog.cles.jp/np_cles )\r
+  * $Id: index.php,v 1.21 2010/06/06 11:44:19 hsur Exp $\r
+*/\r
+\r
+/*\r
+  * Copyright (C) 2004-2010 cles. All rights reserved.\r
+  *\r
+  * This program is free software; you can redistribute it and/or\r
+  * modify it under the terms of the GNU General Public License\r
+  * as published by the Free Software Foundation; either version 2\r
+  * of the License, or (at your option) any later version.\r
+  * \r
+  * This program is distributed in the hope that it will be useful,\r
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  * GNU General Public License for more details.\r
+  * \r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program; if not, write to the Free Software\r
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA\r
+  * \r
+  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives\r
+  * permission to link the code of this program with those files in the PEAR\r
+  * library that are licensed under the PHP License (or with modified versions\r
+  * of those files that use the same license as those files), and distribute\r
+  * linked combinations including the two. You must obey the GNU General Public\r
+  * License in all respects for all of the code used other than those files in\r
+  * the PEAR library that are licensed under the PHP License. If you modify\r
+  * this file, you may extend this exception to your version of the file,\r
+  * but you are not obligated to do so. If you do not wish to do so, delete\r
+  * this exception statement from your version.\r
+*/\r
+\r
+$strRel = '../../../';\r
+include ($strRel.'config.php');\r
+include ($DIR_LIBS.'PLUGINADMIN.php');\r
+\r
+require_once($DIR_PLUGINS . 'sharedlibs/sharedlibs.php');\r
+require_once('cles/Feedback.php');\r
+\r
+$required = '4.3.0';\r
+if( ! version_compare(phpversion() , $required , '>=') ){\r
+       ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');\r
+}\r
+\r
+if ($blogid) {\r
+       $isblogadmin = $member->isBlogAdmin($blogid);\r
+} else\r
+       $isblogadmin = 0;\r
+\r
+if (!$member->isLoggedIn()) {\r
+       $oPluginAdmin = new PluginAdmin('Moblog');\r
+       $oPluginAdmin->start();\r
+       echo "<p>ログインが必要です</p>";\r
+       $oPluginAdmin->end();\r
+       exit;\r
+}\r
+\r
+if (!($member->isAdmin() || $isblogadmin)) {\r
+       $oPluginAdmin = new PluginAdmin('Moblog');\r
+       $oPluginAdmin->start();\r
+       echo "<p>"._ERROR_DISALLOWED."</p>";\r
+       $oPluginAdmin->end();\r
+       exit;\r
+}\r
+\r
+if (isset ($_GET['page'])) {\r
+       $action = getVar('page');\r
+}\r
+if (isset ($_POST['page'])) {\r
+       $action = getVar('page');\r
+}\r
+\r
+// create the admin area page\r
+$oPluginAdmin = new PluginAdmin('Moblog');\r
+$oPluginAdmin->start();\r
+$fb =& new cles_Feedback($oPluginAdmin);\r
+\r
+// menu\r
+echo "<h2>Moblog menu</h2>\n";\r
+echo "<ul>\n";\r
+echo "<li><a href=\"".serverVar('PHP_SELF')."?page=report\"><span style=\"font-weight:bold; color:red\">" . $fb->getMenuStr() . "</span></a></li>\n";\r
+echo "</ul>\n";\r
+\r
+//action\r
+switch ($action) {\r
+       case 'report' :\r
+               $fb->printForm();\r
+               break;\r
+\r
+       default :\r
+               break;\r
+}\r
+\r
+echo "<br />";\r
+\r
+$oPluginAdmin->end();\r
index b2db899..984ffa0 100644 (file)
-<?php
-// +-----------------------------------------------------------------------+
-// | Copyright (c) 2001-2002, Richard Heyes                                |
-// | All rights reserved.                                                  |
-// |                                                                       |
-// | Redistribution and use in source and binary forms, with or without    |
-// | modification, are permitted provided that the following conditions    |
-// | are met:                                                              |
-// |                                                                       |
-// | o Redistributions of source code must retain the above copyright      |
-// |   notice, this list of conditions and the following disclaimer.       |
-// | o Redistributions in binary form must reproduce the above copyright   |
-// |   notice, this list of conditions and the following disclaimer in the |
-// |   documentation and/or other materials provided with the distribution.|
-// | o The names of the authors may not be used to endorse or promote      |
-// |   products derived from this software without specific prior written  |
-// |   permission.                                                         |
-// |                                                                       |
-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
-// |                                                                       |
-// +-----------------------------------------------------------------------+
-// | Authors: Richard Heyes <richard@phpguru.org>                          |
-// |          Chuck Hagenbuch <chuck@horde.org>                            |
-// +-----------------------------------------------------------------------+
-
-/**
- * RFC 822 Email address list validation Utility
- *
- * What is it?
- *
- * This class will take an address string, and parse it into it's consituent
- * parts, be that either addresses, groups, or combinations. Nested groups
- * are not supported. The structure it returns is pretty straight forward,
- * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
- * print_r() to view the structure.
- *
- * How do I use it?
- *
- * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
- * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
- * print_r($structure);
- *
- * @author  Richard Heyes <richard@phpguru.org>
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @version $Revision: 1.1 $
- * @license BSD
- * @package Mail
- */
-class Mail_RFC822 {
-
-    /**
-     * The address being parsed by the RFC822 object.
-     * @var string $address
-     */
-    var $address = '';
-
-    /**
-     * The default domain to use for unqualified addresses.
-     * @var string $default_domain
-     */
-    var $default_domain = 'localhost';
-
-    /**
-     * Should we return a nested array showing groups, or flatten everything?
-     * @var boolean $nestGroups
-     */
-    var $nestGroups = true;
-
-    /**
-     * Whether or not to validate atoms for non-ascii characters.
-     * @var boolean $validate
-     */
-    var $validate = true;
-
-    /**
-     * The array of raw addresses built up as we parse.
-     * @var array $addresses
-     */
-    var $addresses = array();
-
-    /**
-     * The final array of parsed address information that we build up.
-     * @var array $structure
-     */
-    var $structure = array();
-
-    /**
-     * The current error message, if any.
-     * @var string $error
-     */
-    var $error = null;
-
-    /**
-     * An internal counter/pointer.
-     * @var integer $index
-     */
-    var $index = null;
-
-    /**
-     * The number of groups that have been found in the address list.
-     * @var integer $num_groups
-     * @access public
-     */
-    var $num_groups = 0;
-
-    /**
-     * A variable so that we can tell whether or not we're inside a
-     * Mail_RFC822 object.
-     * @var boolean $mailRFC822
-     */
-    var $mailRFC822 = true;
-
-    /**
-    * A limit after which processing stops
-    * @var int $limit
-    */
-    var $limit = null;
-
-    /**
-     * Sets up the object. The address must either be set here or when
-     * calling parseAddressList(). One or the other.
-     *
-     * @access public
-     * @param string  $address         The address(es) to validate.
-     * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.
-     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
-     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
-     *
-     * @return object Mail_RFC822 A new Mail_RFC822 object.
-     */
-    function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
-    {
-        if (isset($address))        $this->address        = $address;
-        if (isset($default_domain)) $this->default_domain = $default_domain;
-        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
-        if (isset($validate))       $this->validate       = $validate;
-        if (isset($limit))          $this->limit          = $limit;
-    }
-
-    /**
-     * Starts the whole process. The address must either be set here
-     * or when creating the object. One or the other.
-     *
-     * @access public
-     * @param string  $address         The address(es) to validate.
-     * @param string  $default_domain  Default domain/host etc.
-     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.
-     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
-     *
-     * @return array A structured array of addresses.
-     */
-    function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
-    {
-        if (!isset($this) || !isset($this->mailRFC822)) {
-            $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
-            return $obj->parseAddressList();
-        }
-
-        if (isset($address))        $this->address        = $address;
-        if (isset($default_domain)) $this->default_domain = $default_domain;
-        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;
-        if (isset($validate))       $this->validate       = $validate;
-        if (isset($limit))          $this->limit          = $limit;
-
-        $this->structure  = array();
-        $this->addresses  = array();
-        $this->error      = null;
-        $this->index      = null;
-
-        // Unfold any long lines in $this->address.
-        $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
-        $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
-
-        while ($this->address = $this->_splitAddresses($this->address));
-
-        if ($this->address === false || isset($this->error)) {
-            require_once 'PEAR.php';
-            return PEAR::raiseError($this->error);
-        }
-
-        // Validate each address individually.  If we encounter an invalid
-        // address, stop iterating and return an error immediately.
-        foreach ($this->addresses as $address) {
-            $valid = $this->_validateAddress($address);
-
-            if ($valid === false || isset($this->error)) {
-                require_once 'PEAR.php';
-                return PEAR::raiseError($this->error);
-            }
-
-            if (!$this->nestGroups) {
-                $this->structure = array_merge($this->structure, $valid);
-            } else {
-                $this->structure[] = $valid;
-            }
-        }
-
-        return $this->structure;
-    }
-
-    /**
-     * Splits an address into separate addresses.
-     *
-     * @access private
-     * @param string $address The addresses to split.
-     * @return boolean Success or failure.
-     */
-    function _splitAddresses($address)
-    {
-        if (!empty($this->limit) && count($this->addresses) == $this->limit) {
-            return '';
-        }
-
-        if ($this->_isGroup($address) && !isset($this->error)) {
-            $split_char = ';';
-            $is_group   = true;
-        } elseif (!isset($this->error)) {
-            $split_char = ',';
-            $is_group   = false;
-        } elseif (isset($this->error)) {
-            return false;
-        }
-
-        // Split the string based on the above ten or so lines.
-        $parts  = explode($split_char, $address);
-        $string = $this->_splitCheck($parts, $split_char);
-
-        // If a group...
-        if ($is_group) {
-            // If $string does not contain a colon outside of
-            // brackets/quotes etc then something's fubar.
-
-            // First check there's a colon at all:
-            if (strpos($string, ':') === false) {
-                $this->error = 'Invalid address: ' . $string;
-                return false;
-            }
-
-            // Now check it's outside of brackets/quotes:
-            if (!$this->_splitCheck(explode(':', $string), ':')) {
-                return false;
-            }
-
-            // We must have a group at this point, so increase the counter:
-            $this->num_groups++;
-        }
-
-        // $string now contains the first full address/group.
-        // Add to the addresses array.
-        $this->addresses[] = array(
-                                   'address' => trim($string),
-                                   'group'   => $is_group
-                                   );
-
-        // Remove the now stored address from the initial line, the +1
-        // is to account for the explode character.
-        $address = trim(substr($address, strlen($string) + 1));
-
-        // If the next char is a comma and this was a group, then
-        // there are more addresses, otherwise, if there are any more
-        // chars, then there is another address.
-        if ($is_group && substr($address, 0, 1) == ','){
-            $address = trim(substr($address, 1));
-            return $address;
-
-        } elseif (strlen($address) > 0) {
-            return $address;
-
-        } else {
-            return '';
-        }
-
-        // If you got here then something's off
-        return false;
-    }
-
-    /**
-     * Checks for a group at the start of the string.
-     *
-     * @access private
-     * @param string $address The address to check.
-     * @return boolean Whether or not there is a group at the start of the string.
-     */
-    function _isGroup($address)
-    {
-        // First comma not in quotes, angles or escaped:
-        $parts  = explode(',', $address);
-        $string = $this->_splitCheck($parts, ',');
-
-        // Now we have the first address, we can reliably check for a
-        // group by searching for a colon that's not escaped or in
-        // quotes or angle brackets.
-        if (count($parts = explode(':', $string)) > 1) {
-            $string2 = $this->_splitCheck($parts, ':');
-            return ($string2 !== $string);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * A common function that will check an exploded string.
-     *
-     * @access private
-     * @param array $parts The exloded string.
-     * @param string $char  The char that was exploded on.
-     * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
-     */
-    function _splitCheck($parts, $char)
-    {
-        $string = $parts[0];
-
-        for ($i = 0; $i < count($parts); $i++) {
-            if ($this->_hasUnclosedQuotes($string)
-                || $this->_hasUnclosedBrackets($string, '<>')
-                || $this->_hasUnclosedBrackets($string, '[]')
-                || $this->_hasUnclosedBrackets($string, '()')
-                || substr($string, -1) == '\\') {
-                if (isset($parts[$i + 1])) {
-                    $string = $string . $char . $parts[$i + 1];
-                } else {
-                    $this->error = 'Invalid address spec. Unclosed bracket or quotes';
-                    return false;
-                }
-            } else {
-                $this->index = $i;
-                break;
-            }
-        }
-
-        return $string;
-    }
-
-    /**
-     * Checks if a string has an unclosed quotes or not.
-     *
-     * @access private
-     * @param string $string The string to check.
-     * @return boolean True if there are unclosed quotes inside the string, false otherwise.
-     */
-    function _hasUnclosedQuotes($string)
-    {
-        $string     = explode('"', $string);
-        $string_cnt = count($string);
-
-        for ($i = 0; $i < (count($string) - 1); $i++)
-            if (substr($string[$i], -1) == '\\')
-                $string_cnt--;
-
-        return ($string_cnt % 2 === 0);
-    }
-
-    /**
-     * Checks if a string has an unclosed brackets or not. IMPORTANT:
-     * This function handles both angle brackets and square brackets;
-     *
-     * @access private
-     * @param string $string The string to check.
-     * @param string $chars  The characters to check for.
-     * @return boolean True if there are unclosed brackets inside the string, false otherwise.
-     */
-    function _hasUnclosedBrackets($string, $chars)
-    {
-        $num_angle_start = substr_count($string, $chars[0]);
-        $num_angle_end   = substr_count($string, $chars[1]);
-
-        $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
-        $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
-
-        if ($num_angle_start < $num_angle_end) {
-            $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
-            return false;
-        } else {
-            return ($num_angle_start > $num_angle_end);
-        }
-    }
-
-    /**
-     * Sub function that is used only by hasUnclosedBrackets().
-     *
-     * @access private
-     * @param string $string The string to check.
-     * @param integer &$num    The number of occurences.
-     * @param string $char   The character to count.
-     * @return integer The number of occurences of $char in $string, adjusted for backslashes.
-     */
-    function _hasUnclosedBracketsSub($string, &$num, $char)
-    {
-        $parts = explode($char, $string);
-        for ($i = 0; $i < count($parts); $i++){
-            if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
-                $num--;
-            if (isset($parts[$i + 1]))
-                $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
-        }
-
-        return $num;
-    }
-
-    /**
-     * Function to begin checking the address.
-     *
-     * @access private
-     * @param string $address The address to validate.
-     * @return mixed False on failure, or a structured array of address information on success.
-     */
-    function _validateAddress($address)
-    {
-        $is_group = false;
-        $addresses = array();
-
-        if ($address['group']) {
-            $is_group = true;
-
-            // Get the group part of the name
-            $parts     = explode(':', $address['address']);
-            $groupname = $this->_splitCheck($parts, ':');
-            $structure = array();
-
-            // And validate the group part of the name.
-            if (!$this->_validatePhrase($groupname)){
-                $this->error = 'Group name did not validate.';
-                return false;
-            } else {
-                // Don't include groups if we are not nesting
-                // them. This avoids returning invalid addresses.
-                if ($this->nestGroups) {
-                    $structure = new stdClass;
-                    $structure->groupname = $groupname;
-                }
-            }
-
-            $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
-        }
-
-        // If a group then split on comma and put into an array.
-        // Otherwise, Just put the whole address in an array.
-        if ($is_group) {
-            while (strlen($address['address']) > 0) {
-                $parts       = explode(',', $address['address']);
-                $addresses[] = $this->_splitCheck($parts, ',');
-                $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
-            }
-        } else {
-            $addresses[] = $address['address'];
-        }
-
-        // Check that $addresses is set, if address like this:
-        // Groupname:;
-        // Then errors were appearing.
-        if (!count($addresses)){
-            $this->error = 'Empty group.';
-            return false;
-        }
-
-        // Trim the whitespace from all of the address strings.
-        array_map('trim', $addresses);
-
-        // Validate each mailbox.
-        // Format could be one of: name <geezer@domain.com>
-        //                         geezer@domain.com
-        //                         geezer
-        // ... or any other format valid by RFC 822.
-        for ($i = 0; $i < count($addresses); $i++) {
-            if (!$this->validateMailbox($addresses[$i])) {
-                if (empty($this->error)) {
-                    $this->error = 'Validation failed for: ' . $addresses[$i];
-                }
-                return false;
-            }
-        }
-
-        // Nested format
-        if ($this->nestGroups) {
-            if ($is_group) {
-                $structure->addresses = $addresses;
-            } else {
-                $structure = $addresses[0];
-            }
-
-        // Flat format
-        } else {
-            if ($is_group) {
-                $structure = array_merge($structure, $addresses);
-            } else {
-                $structure = $addresses;
-            }
-        }
-
-        return $structure;
-    }
-
-    /**
-     * Function to validate a phrase.
-     *
-     * @access private
-     * @param string $phrase The phrase to check.
-     * @return boolean Success or failure.
-     */
-    function _validatePhrase($phrase)
-    {
-        // Splits on one or more Tab or space.
-        $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
-
-        $phrase_parts = array();
-        while (count($parts) > 0){
-            $phrase_parts[] = $this->_splitCheck($parts, ' ');
-            for ($i = 0; $i < $this->index + 1; $i++)
-                array_shift($parts);
-        }
-
-        foreach ($phrase_parts as $part) {
-            // If quoted string:
-            if (substr($part, 0, 1) == '"') {
-                if (!$this->_validateQuotedString($part)) {
-                    return false;
-                }
-                continue;
-            }
-
-            // Otherwise it's an atom:
-            if (!$this->_validateAtom($part)) return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Function to validate an atom which from rfc822 is:
-     * atom = 1*<any CHAR except specials, SPACE and CTLs>
-     *
-     * If validation ($this->validate) has been turned off, then
-     * validateAtom() doesn't actually check anything. This is so that you
-     * can split a list of addresses up before encoding personal names
-     * (umlauts, etc.), for example.
-     *
-     * @access private
-     * @param string $atom The string to check.
-     * @return boolean Success or failure.
-     */
-    function _validateAtom($atom)
-    {
-        if (!$this->validate) {
-            // Validation has been turned off; assume the atom is okay.
-            return true;
-        }
-
-        // Check for any char from ASCII 0 - ASCII 127
-        if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
-            return false;
-        }
-
-        // Check for specials:
-        if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
-            return false;
-        }
-
-        // Check for control characters (ASCII 0-31):
-        if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Function to validate quoted string, which is:
-     * quoted-string = <"> *(qtext/quoted-pair) <">
-     *
-     * @access private
-     * @param string $qstring The string to check
-     * @return boolean Success or failure.
-     */
-    function _validateQuotedString($qstring)
-    {
-        // Leading and trailing "
-        $qstring = substr($qstring, 1, -1);
-
-        // Perform check, removing quoted characters first.
-        return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
-    }
-
-    /**
-     * Function to validate a mailbox, which is:
-     * mailbox =   addr-spec         ; simple address
-     *           / phrase route-addr ; name and route-addr
-     *
-     * @access public
-     * @param string &$mailbox The string to check.
-     * @return boolean Success or failure.
-     */
-    function validateMailbox(&$mailbox)
-    {
-        // A couple of defaults.
-        $phrase  = '';
-        $comment = '';
-        $comments = array();
-
-        // Catch any RFC822 comments and store them separately.
-        $_mailbox = $mailbox;
-        while (strlen(trim($_mailbox)) > 0) {
-            $parts = explode('(', $_mailbox);
-            $before_comment = $this->_splitCheck($parts, '(');
-            if ($before_comment != $_mailbox) {
-                // First char should be a (.
-                $comment    = substr(str_replace($before_comment, '', $_mailbox), 1);
-                $parts      = explode(')', $comment);
-                $comment    = $this->_splitCheck($parts, ')');
-                $comments[] = $comment;
-
-                // +1 is for the trailing )
-                $_mailbox   = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);
-            } else {
-                break;
-            }
-        }
-
-        foreach ($comments as $comment) {
-            $mailbox = str_replace("($comment)", '', $mailbox);
-        }
-
-        $mailbox = trim($mailbox);
-
-        // Check for name + route-addr
-        if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
-            $parts  = explode('<', $mailbox);
-            $name   = $this->_splitCheck($parts, '<');
-
-            $phrase     = trim($name);
-            $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
-
-            if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
-                return false;
-            }
-
-        // Only got addr-spec
-        } else {
-            // First snip angle brackets if present.
-            if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
-                $addr_spec = substr($mailbox, 1, -1);
-            } else {
-                $addr_spec = $mailbox;
-            }
-
-            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
-                return false;
-            }
-        }
-
-        // Construct the object that will be returned.
-        $mbox = new stdClass();
-
-        // Add the phrase (even if empty) and comments
-        $mbox->personal = $phrase;
-        $mbox->comment  = isset($comments) ? $comments : array();
-
-        if (isset($route_addr)) {
-            $mbox->mailbox = $route_addr['local_part'];
-            $mbox->host    = $route_addr['domain'];
-            $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
-        } else {
-            $mbox->mailbox = $addr_spec['local_part'];
-            $mbox->host    = $addr_spec['domain'];
-        }
-
-        $mailbox = $mbox;
-        return true;
-    }
-
-    /**
-     * This function validates a route-addr which is:
-     * route-addr = "<" [route] addr-spec ">"
-     *
-     * Angle brackets have already been removed at the point of
-     * getting to this function.
-     *
-     * @access private
-     * @param string $route_addr The string to check.
-     * @return mixed False on failure, or an array containing validated address/route information on success.
-     */
-    function _validateRouteAddr($route_addr)
-    {
-        // Check for colon.
-        if (strpos($route_addr, ':') !== false) {
-            $parts = explode(':', $route_addr);
-            $route = $this->_splitCheck($parts, ':');
-        } else {
-            $route = $route_addr;
-        }
-
-        // If $route is same as $route_addr then the colon was in
-        // quotes or brackets or, of course, non existent.
-        if ($route === $route_addr){
-            unset($route);
-            $addr_spec = $route_addr;
-            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
-                return false;
-            }
-        } else {
-            // Validate route part.
-            if (($route = $this->_validateRoute($route)) === false) {
-                return false;
-            }
-
-            $addr_spec = substr($route_addr, strlen($route . ':'));
-
-            // Validate addr-spec part.
-            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
-                return false;
-            }
-        }
-
-        if (isset($route)) {
-            $return['adl'] = $route;
-        } else {
-            $return['adl'] = '';
-        }
-
-        $return = array_merge($return, $addr_spec);
-        return $return;
-    }
-
-    /**
-     * Function to validate a route, which is:
-     * route = 1#("@" domain) ":"
-     *
-     * @access private
-     * @param string $route The string to check.
-     * @return mixed False on failure, or the validated $route on success.
-     */
-    function _validateRoute($route)
-    {
-        // Split on comma.
-        $domains = explode(',', trim($route));
-
-        foreach ($domains as $domain) {
-            $domain = str_replace('@', '', trim($domain));
-            if (!$this->_validateDomain($domain)) return false;
-        }
-
-        return $route;
-    }
-
-    /**
-     * Function to validate a domain, though this is not quite what
-     * you expect of a strict internet domain.
-     *
-     * domain = sub-domain *("." sub-domain)
-     *
-     * @access private
-     * @param string $domain The string to check.
-     * @return mixed False on failure, or the validated domain on success.
-     */
-    function _validateDomain($domain)
-    {
-        // Note the different use of $subdomains and $sub_domains
-        $subdomains = explode('.', $domain);
-
-        while (count($subdomains) > 0) {
-            $sub_domains[] = $this->_splitCheck($subdomains, '.');
-            for ($i = 0; $i < $this->index + 1; $i++)
-                array_shift($subdomains);
-        }
-
-        foreach ($sub_domains as $sub_domain) {
-            if (!$this->_validateSubdomain(trim($sub_domain)))
-                return false;
-        }
-
-        // Managed to get here, so return input.
-        return $domain;
-    }
-
-    /**
-     * Function to validate a subdomain:
-     *   subdomain = domain-ref / domain-literal
-     *
-     * @access private
-     * @param string $subdomain The string to check.
-     * @return boolean Success or failure.
-     */
-    function _validateSubdomain($subdomain)
-    {
-        if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
-            if (!$this->_validateDliteral($arr[1])) return false;
-        } else {
-            if (!$this->_validateAtom($subdomain)) return false;
-        }
-
-        // Got here, so return successful.
-        return true;
-    }
-
-    /**
-     * Function to validate a domain literal:
-     *   domain-literal =  "[" *(dtext / quoted-pair) "]"
-     *
-     * @access private
-     * @param string $dliteral The string to check.
-     * @return boolean Success or failure.
-     */
-    function _validateDliteral($dliteral)
-    {
-        return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
-    }
-
-    /**
-     * Function to validate an addr-spec.
-     *
-     * addr-spec = local-part "@" domain
-     *
-     * @access private
-     * @param string $addr_spec The string to check.
-     * @return mixed False on failure, or the validated addr-spec on success.
-     */
-    function _validateAddrSpec($addr_spec)
-    {
-        $addr_spec = trim($addr_spec);
-
-        // Split on @ sign if there is one.
-        if (strpos($addr_spec, '@') !== false) {
-            $parts      = explode('@', $addr_spec);
-            $local_part = $this->_splitCheck($parts, '@');
-            $domain     = substr($addr_spec, strlen($local_part . '@'));
-
-        // No @ sign so assume the default domain.
-        } else {
-            $local_part = $addr_spec;
-            $domain     = $this->default_domain;
-        }
-
-        if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
-        if (($domain     = $this->_validateDomain($domain)) === false) return false;
-
-        // Got here so return successful.
-        return array('local_part' => $local_part, 'domain' => $domain);
-    }
-
-    /**
-     * Function to validate the local part of an address:
-     *   local-part = word *("." word)
-     *
-     * @access private
-     * @param string $local_part
-     * @return mixed False on failure, or the validated local part on success.
-     */
-    function _validateLocalPart($local_part)
-    {
-        $parts = explode('.', $local_part);
-        $words = array();
-
-        // Split the local_part into words.
-        while (count($parts) > 0){
-            $words[] = $this->_splitCheck($parts, '.');
-            for ($i = 0; $i < $this->index + 1; $i++) {
-                array_shift($parts);
-            }
-        }
-
-        // Validate each word.
-        foreach ($words as $word) {
-            // If this word contains an unquoted space, it is invalid. (6.2.4)
-            if (strpos($word, ' ') && $word[0] !== '"')
-            {
-                return false;
-            }
-
-            if ($this->_validatePhrase(trim($word)) === false) return false;
-        }
-
-        // Managed to get here, so return the input.
-        return $local_part;
-    }
-
-    /**
-     * Returns an approximate count of how many addresses are in the
-     * given string. This is APPROXIMATE as it only splits based on a
-     * comma which has no preceding backslash. Could be useful as
-     * large amounts of addresses will end up producing *large*
-     * structures when used with parseAddressList().
-     *
-     * @param  string $data Addresses to count
-     * @return int          Approximate count
-     */
-    function approximateCount($data)
-    {
-        return count(preg_split('/(?<!\\\\),/', $data));
-    }
-
-    /**
-     * This is a email validating function separate to the rest of the
-     * class. It simply validates whether an email is of the common
-     * internet form: <user>@<domain>. This can be sufficient for most
-     * people. Optional stricter mode can be utilised which restricts
-     * mailbox characters allowed to alphanumeric, full stop, hyphen
-     * and underscore.
-     *
-     * @param  string  $data   Address to check
-     * @param  boolean $strict Optional stricter mode
-     * @return mixed           False if it fails, an indexed array
-     *                         username/domain if it matches
-     */
-    function isValidInetAddress($data, $strict = false)
-    {
-        $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,4})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,4})$/i';
-        if (preg_match($regex, trim($data), $matches)) {
-            return array($matches[1], $matches[2]);
-        } else {
-            return false;
-        }
-    }
-
-}
+<?php\r
+// +-----------------------------------------------------------------------+\r
+// | Copyright (c) 2001-2002, Richard Heyes                                |\r
+// | All rights reserved.                                                  |\r
+// |                                                                       |\r
+// | Redistribution and use in source and binary forms, with or without    |\r
+// | modification, are permitted provided that the following conditions    |\r
+// | are met:                                                              |\r
+// |                                                                       |\r
+// | o Redistributions of source code must retain the above copyright      |\r
+// |   notice, this list of conditions and the following disclaimer.       |\r
+// | o Redistributions in binary form must reproduce the above copyright   |\r
+// |   notice, this list of conditions and the following disclaimer in the |\r
+// |   documentation and/or other materials provided with the distribution.|\r
+// | o The names of the authors may not be used to endorse or promote      |\r
+// |   products derived from this software without specific prior written  |\r
+// |   permission.                                                         |\r
+// |                                                                       |\r
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |\r
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |\r
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |\r
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |\r
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |\r
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |\r
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |\r
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |\r
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |\r
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |\r
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |\r
+// |                                                                       |\r
+// +-----------------------------------------------------------------------+\r
+// | Authors: Richard Heyes <richard@phpguru.org>                          |\r
+// |          Chuck Hagenbuch <chuck@horde.org>                            |\r
+// +-----------------------------------------------------------------------+\r
+\r
+/**\r
+ * RFC 822 Email address list validation Utility\r
+ *\r
+ * What is it?\r
+ *\r
+ * This class will take an address string, and parse it into it's consituent\r
+ * parts, be that either addresses, groups, or combinations. Nested groups\r
+ * are not supported. The structure it returns is pretty straight forward,\r
+ * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use\r
+ * print_r() to view the structure.\r
+ *\r
+ * How do I use it?\r
+ *\r
+ * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';\r
+ * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)\r
+ * print_r($structure);\r
+ *\r
+ * @author  Richard Heyes <richard@phpguru.org>\r
+ * @author  Chuck Hagenbuch <chuck@horde.org>\r
+ * @version $Revision: 1.7 $\r
+ * @license BSD\r
+ * @package Mail\r
+ */\r
+class Mail_RFC822 {\r
+\r
+    /**\r
+     * The address being parsed by the RFC822 object.\r
+     * @var string $address\r
+     */\r
+    var $address = '';\r
+\r
+    /**\r
+     * The default domain to use for unqualified addresses.\r
+     * @var string $default_domain\r
+     */\r
+    var $default_domain = 'localhost';\r
+\r
+    /**\r
+     * Should we return a nested array showing groups, or flatten everything?\r
+     * @var boolean $nestGroups\r
+     */\r
+    var $nestGroups = true;\r
+\r
+    /**\r
+     * Whether or not to validate atoms for non-ascii characters.\r
+     * @var boolean $validate\r
+     */\r
+    var $validate = true;\r
+\r
+    /**\r
+     * The array of raw addresses built up as we parse.\r
+     * @var array $addresses\r
+     */\r
+    var $addresses = array();\r
+\r
+    /**\r
+     * The final array of parsed address information that we build up.\r
+     * @var array $structure\r
+     */\r
+    var $structure = array();\r
+\r
+    /**\r
+     * The current error message, if any.\r
+     * @var string $error\r
+     */\r
+    var $error = null;\r
+\r
+    /**\r
+     * An internal counter/pointer.\r
+     * @var integer $index\r
+     */\r
+    var $index = null;\r
+\r
+    /**\r
+     * The number of groups that have been found in the address list.\r
+     * @var integer $num_groups\r
+     * @access public\r
+     */\r
+    var $num_groups = 0;\r
+\r
+    /**\r
+     * A variable so that we can tell whether or not we're inside a\r
+     * Mail_RFC822 object.\r
+     * @var boolean $mailRFC822\r
+     */\r
+    var $mailRFC822 = true;\r
+\r
+    /**\r
+    * A limit after which processing stops\r
+    * @var int $limit\r
+    */\r
+    var $limit = null;\r
+\r
+    /**\r
+     * Sets up the object. The address must either be set here or when\r
+     * calling parseAddressList(). One or the other.\r
+     *\r
+     * @access public\r
+     * @param string  $address         The address(es) to validate.\r
+     * @param string  $default_domain  Default domain/host etc. If not supplied, will be set to localhost.\r
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.\r
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.\r
+     *\r
+     * @return object Mail_RFC822 A new Mail_RFC822 object.\r
+     */\r
+    function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)\r
+    {\r
+        if (isset($address))        $this->address        = $address;\r
+        if (isset($default_domain)) $this->default_domain = $default_domain;\r
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;\r
+        if (isset($validate))       $this->validate       = $validate;\r
+        if (isset($limit))          $this->limit          = $limit;\r
+    }\r
+\r
+    /**\r
+     * Starts the whole process. The address must either be set here\r
+     * or when creating the object. One or the other.\r
+     *\r
+     * @access public\r
+     * @param string  $address         The address(es) to validate.\r
+     * @param string  $default_domain  Default domain/host etc.\r
+     * @param boolean $nest_groups     Whether to return the structure with groups nested for easier viewing.\r
+     * @param boolean $validate        Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.\r
+     *\r
+     * @return array A structured array of addresses.\r
+     */\r
+    function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)\r
+    {\r
+        if (!isset($this) || !isset($this->mailRFC822)) {\r
+            $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);\r
+            return $obj->parseAddressList();\r
+        }\r
+\r
+        if (isset($address))        $this->address        = $address;\r
+        if (isset($default_domain)) $this->default_domain = $default_domain;\r
+        if (isset($nest_groups))    $this->nestGroups     = $nest_groups;\r
+        if (isset($validate))       $this->validate       = $validate;\r
+        if (isset($limit))          $this->limit          = $limit;\r
+\r
+        $this->structure  = array();\r
+        $this->addresses  = array();\r
+        $this->error      = null;\r
+        $this->index      = null;\r
+\r
+        // Unfold any long lines in $this->address.\r
+        $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);\r
+        $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);\r
+\r
+        while ($this->address = $this->_splitAddresses($this->address));\r
+\r
+        if ($this->address === false || isset($this->error)) {\r
+            require_once 'PEAR.php';\r
+            return PEAR::raiseError($this->error);\r
+        }\r
+\r
+        // Validate each address individually.  If we encounter an invalid\r
+        // address, stop iterating and return an error immediately.\r
+        foreach ($this->addresses as $address) {\r
+            $valid = $this->_validateAddress($address);\r
+\r
+            if ($valid === false || isset($this->error)) {\r
+                require_once 'PEAR.php';\r
+                return PEAR::raiseError($this->error);\r
+            }\r
+\r
+            if (!$this->nestGroups) {\r
+                $this->structure = array_merge($this->structure, $valid);\r
+            } else {\r
+                $this->structure[] = $valid;\r
+            }\r
+        }\r
+\r
+        return $this->structure;\r
+    }\r
+\r
+    /**\r
+     * Splits an address into separate addresses.\r
+     *\r
+     * @access private\r
+     * @param string $address The addresses to split.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _splitAddresses($address)\r
+    {\r
+        if (!empty($this->limit) && count($this->addresses) == $this->limit) {\r
+            return '';\r
+        }\r
+\r
+        if ($this->_isGroup($address) && !isset($this->error)) {\r
+            $split_char = ';';\r
+            $is_group   = true;\r
+        } elseif (!isset($this->error)) {\r
+            $split_char = ',';\r
+            $is_group   = false;\r
+        } elseif (isset($this->error)) {\r
+            return false;\r
+        }\r
+\r
+        // Split the string based on the above ten or so lines.\r
+        $parts  = explode($split_char, $address);\r
+        $string = $this->_splitCheck($parts, $split_char);\r
+\r
+        // If a group...\r
+        if ($is_group) {\r
+            // If $string does not contain a colon outside of\r
+            // brackets/quotes etc then something's fubar.\r
+\r
+            // First check there's a colon at all:\r
+            if (strpos($string, ':') === false) {\r
+                $this->error = 'Invalid address: ' . $string;\r
+                return false;\r
+            }\r
+\r
+            // Now check it's outside of brackets/quotes:\r
+            if (!$this->_splitCheck(explode(':', $string), ':')) {\r
+                return false;\r
+            }\r
+\r
+            // We must have a group at this point, so increase the counter:\r
+            $this->num_groups++;\r
+        }\r
+\r
+        // $string now contains the first full address/group.\r
+        // Add to the addresses array.\r
+        $this->addresses[] = array(\r
+                                   'address' => trim($string),\r
+                                   'group'   => $is_group\r
+                                   );\r
+\r
+        // Remove the now stored address from the initial line, the +1\r
+        // is to account for the explode character.\r
+        $address = trim(substr($address, strlen($string) + 1));\r
+\r
+        // If the next char is a comma and this was a group, then\r
+        // there are more addresses, otherwise, if there are any more\r
+        // chars, then there is another address.\r
+        if ($is_group && substr($address, 0, 1) == ','){\r
+            $address = trim(substr($address, 1));\r
+            return $address;\r
+\r
+        } elseif (strlen($address) > 0) {\r
+            return $address;\r
+\r
+        } else {\r
+            return '';\r
+        }\r
+\r
+        // If you got here then something's off\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Checks for a group at the start of the string.\r
+     *\r
+     * @access private\r
+     * @param string $address The address to check.\r
+     * @return boolean Whether or not there is a group at the start of the string.\r
+     */\r
+    function _isGroup($address)\r
+    {\r
+        // First comma not in quotes, angles or escaped:\r
+        $parts  = explode(',', $address);\r
+        $string = $this->_splitCheck($parts, ',');\r
+\r
+        // Now we have the first address, we can reliably check for a\r
+        // group by searching for a colon that's not escaped or in\r
+        // quotes or angle brackets.\r
+        if (count($parts = explode(':', $string)) > 1) {\r
+            $string2 = $this->_splitCheck($parts, ':');\r
+            return ($string2 !== $string);\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * A common function that will check an exploded string.\r
+     *\r
+     * @access private\r
+     * @param array $parts The exloded string.\r
+     * @param string $char  The char that was exploded on.\r
+     * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.\r
+     */\r
+    function _splitCheck($parts, $char)\r
+    {\r
+        $string = $parts[0];\r
+\r
+        for ($i = 0; $i < count($parts); $i++) {\r
+            if ($this->_hasUnclosedQuotes($string)\r
+                || $this->_hasUnclosedBrackets($string, '<>')\r
+                || $this->_hasUnclosedBrackets($string, '[]')\r
+                || $this->_hasUnclosedBrackets($string, '()')\r
+                || substr($string, -1) == '\\') {\r
+                if (isset($parts[$i + 1])) {\r
+                    $string = $string . $char . $parts[$i + 1];\r
+                } else {\r
+                    $this->error = 'Invalid address spec. Unclosed bracket or quotes';\r
+                    return false;\r
+                }\r
+            } else {\r
+                $this->index = $i;\r
+                break;\r
+            }\r
+        }\r
+\r
+        return $string;\r
+    }\r
+\r
+    /**\r
+     * Checks if a string has an unclosed quotes or not.\r
+     *\r
+     * @access private\r
+     * @param string $string The string to check.\r
+     * @return boolean True if there are unclosed quotes inside the string, false otherwise.\r
+     */\r
+    function _hasUnclosedQuotes($string)\r
+    {\r
+        $string     = explode('"', $string);\r
+        $string_cnt = count($string);\r
+\r
+        for ($i = 0; $i < (count($string) - 1); $i++)\r
+            if (substr($string[$i], -1) == '\\')\r
+                $string_cnt--;\r
+\r
+        return ($string_cnt % 2 === 0);\r
+    }\r
+\r
+    /**\r
+     * Checks if a string has an unclosed brackets or not. IMPORTANT:\r
+     * This function handles both angle brackets and square brackets;\r
+     *\r
+     * @access private\r
+     * @param string $string The string to check.\r
+     * @param string $chars  The characters to check for.\r
+     * @return boolean True if there are unclosed brackets inside the string, false otherwise.\r
+     */\r
+    function _hasUnclosedBrackets($string, $chars)\r
+    {\r
+        $num_angle_start = substr_count($string, $chars[0]);\r
+        $num_angle_end   = substr_count($string, $chars[1]);\r
+\r
+        $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);\r
+        $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);\r
+\r
+        if ($num_angle_start < $num_angle_end) {\r
+            $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';\r
+            return false;\r
+        } else {\r
+            return ($num_angle_start > $num_angle_end);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Sub function that is used only by hasUnclosedBrackets().\r
+     *\r
+     * @access private\r
+     * @param string $string The string to check.\r
+     * @param integer &$num    The number of occurences.\r
+     * @param string $char   The character to count.\r
+     * @return integer The number of occurences of $char in $string, adjusted for backslashes.\r
+     */\r
+    function _hasUnclosedBracketsSub($string, &$num, $char)\r
+    {\r
+        $parts = explode($char, $string);\r
+        for ($i = 0; $i < count($parts); $i++){\r
+            if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))\r
+                $num--;\r
+            if (isset($parts[$i + 1]))\r
+                $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];\r
+        }\r
+\r
+        return $num;\r
+    }\r
+\r
+    /**\r
+     * Function to begin checking the address.\r
+     *\r
+     * @access private\r
+     * @param string $address The address to validate.\r
+     * @return mixed False on failure, or a structured array of address information on success.\r
+     */\r
+    function _validateAddress($address)\r
+    {\r
+        $is_group = false;\r
+        $addresses = array();\r
+\r
+        if ($address['group']) {\r
+            $is_group = true;\r
+\r
+            // Get the group part of the name\r
+            $parts     = explode(':', $address['address']);\r
+            $groupname = $this->_splitCheck($parts, ':');\r
+            $structure = array();\r
+\r
+            // And validate the group part of the name.\r
+            if (!$this->_validatePhrase($groupname)){\r
+                $this->error = 'Group name did not validate.';\r
+                return false;\r
+            } else {\r
+                // Don't include groups if we are not nesting\r
+                // them. This avoids returning invalid addresses.\r
+                if ($this->nestGroups) {\r
+                    $structure = new stdClass;\r
+                    $structure->groupname = $groupname;\r
+                }\r
+            }\r
+\r
+            $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));\r
+        }\r
+\r
+        // If a group then split on comma and put into an array.\r
+        // Otherwise, Just put the whole address in an array.\r
+        if ($is_group) {\r
+            while (strlen($address['address']) > 0) {\r
+                $parts       = explode(',', $address['address']);\r
+                $addresses[] = $this->_splitCheck($parts, ',');\r
+                $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));\r
+            }\r
+        } else {\r
+            $addresses[] = $address['address'];\r
+        }\r
+\r
+        // Check that $addresses is set, if address like this:\r
+        // Groupname:;\r
+        // Then errors were appearing.\r
+        if (!count($addresses)){\r
+            $this->error = 'Empty group.';\r
+            return false;\r
+        }\r
+\r
+        // Trim the whitespace from all of the address strings.\r
+        array_map('trim', $addresses);\r
+\r
+        // Validate each mailbox.\r
+        // Format could be one of: name <geezer@domain.com>\r
+        //                         geezer@domain.com\r
+        //                         geezer\r
+        // ... or any other format valid by RFC 822.\r
+        for ($i = 0; $i < count($addresses); $i++) {\r
+            if (!$this->validateMailbox($addresses[$i])) {\r
+                if (empty($this->error)) {\r
+                    $this->error = 'Validation failed for: ' . $addresses[$i];\r
+                }\r
+                return false;\r
+            }\r
+        }\r
+\r
+        // Nested format\r
+        if ($this->nestGroups) {\r
+            if ($is_group) {\r
+                $structure->addresses = $addresses;\r
+            } else {\r
+                $structure = $addresses[0];\r
+            }\r
+\r
+        // Flat format\r
+        } else {\r
+            if ($is_group) {\r
+                $structure = array_merge($structure, $addresses);\r
+            } else {\r
+                $structure = $addresses;\r
+            }\r
+        }\r
+\r
+        return $structure;\r
+    }\r
+\r
+    /**\r
+     * Function to validate a phrase.\r
+     *\r
+     * @access private\r
+     * @param string $phrase The phrase to check.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _validatePhrase($phrase)\r
+    {\r
+        // Splits on one or more Tab or space.\r
+        $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);\r
+\r
+        $phrase_parts = array();\r
+        while (count($parts) > 0){\r
+            $phrase_parts[] = $this->_splitCheck($parts, ' ');\r
+            for ($i = 0; $i < $this->index + 1; $i++)\r
+                array_shift($parts);\r
+        }\r
+\r
+        foreach ($phrase_parts as $part) {\r
+            // If quoted string:\r
+            if (substr($part, 0, 1) == '"') {\r
+                if (!$this->_validateQuotedString($part)) {\r
+                    return false;\r
+                }\r
+                continue;\r
+            }\r
+\r
+            // Otherwise it's an atom:\r
+            if (!$this->_validateAtom($part)) return false;\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Function to validate an atom which from rfc822 is:\r
+     * atom = 1*<any CHAR except specials, SPACE and CTLs>\r
+     *\r
+     * If validation ($this->validate) has been turned off, then\r
+     * validateAtom() doesn't actually check anything. This is so that you\r
+     * can split a list of addresses up before encoding personal names\r
+     * (umlauts, etc.), for example.\r
+     *\r
+     * @access private\r
+     * @param string $atom The string to check.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _validateAtom($atom)\r
+    {\r
+        if (!$this->validate) {\r
+            // Validation has been turned off; assume the atom is okay.\r
+            return true;\r
+        }\r
+\r
+        // Check for any char from ASCII 0 - ASCII 127\r
+        if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {\r
+            return false;\r
+        }\r
+\r
+        // Check for specials:\r
+        if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {\r
+            return false;\r
+        }\r
+\r
+        // Check for control characters (ASCII 0-31):\r
+        if (preg_match('/[\\x00-\\x1F]+/', $atom)) {\r
+            return false;\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Function to validate quoted string, which is:\r
+     * quoted-string = <"> *(qtext/quoted-pair) <">\r
+     *\r
+     * @access private\r
+     * @param string $qstring The string to check\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _validateQuotedString($qstring)\r
+    {\r
+        // Leading and trailing "\r
+        $qstring = substr($qstring, 1, -1);\r
+\r
+        // Perform check, removing quoted characters first.\r
+        return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));\r
+    }\r
+\r
+    /**\r
+     * Function to validate a mailbox, which is:\r
+     * mailbox =   addr-spec         ; simple address\r
+     *           / phrase route-addr ; name and route-addr\r
+     *\r
+     * @access public\r
+     * @param string &$mailbox The string to check.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function validateMailbox(&$mailbox)\r
+    {\r
+        // A couple of defaults.\r
+        $phrase  = '';\r
+        $comment = '';\r
+        $comments = array();\r
+\r
+        // Catch any RFC822 comments and store them separately.\r
+        $_mailbox = $mailbox;\r
+        while (strlen(trim($_mailbox)) > 0) {\r
+            $parts = explode('(', $_mailbox);\r
+            $before_comment = $this->_splitCheck($parts, '(');\r
+            if ($before_comment != $_mailbox) {\r
+                // First char should be a (.\r
+                $comment    = substr(str_replace($before_comment, '', $_mailbox), 1);\r
+                $parts      = explode(')', $comment);\r
+                $comment    = $this->_splitCheck($parts, ')');\r
+                $comments[] = $comment;\r
+\r
+                // +1 is for the trailing )\r
+                $_mailbox   = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);\r
+            } else {\r
+                break;\r
+            }\r
+        }\r
+\r
+        foreach ($comments as $comment) {\r
+            $mailbox = str_replace("($comment)", '', $mailbox);\r
+        }\r
+\r
+        $mailbox = trim($mailbox);\r
+\r
+        // Check for name + route-addr\r
+        if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {\r
+            $parts  = explode('<', $mailbox);\r
+            $name   = $this->_splitCheck($parts, '<');\r
+\r
+            $phrase     = trim($name);\r
+            $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));\r
+\r
+            if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {\r
+                return false;\r
+            }\r
+\r
+        // Only got addr-spec\r
+        } else {\r
+            // First snip angle brackets if present.\r
+            if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {\r
+                $addr_spec = substr($mailbox, 1, -1);\r
+            } else {\r
+                $addr_spec = $mailbox;\r
+            }\r
+\r
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
+                return false;\r
+            }\r
+        }\r
+\r
+        // Construct the object that will be returned.\r
+        $mbox = new stdClass();\r
+\r
+        // Add the phrase (even if empty) and comments\r
+        $mbox->personal = $phrase;\r
+        $mbox->comment  = isset($comments) ? $comments : array();\r
+\r
+        if (isset($route_addr)) {\r
+            $mbox->mailbox = $route_addr['local_part'];\r
+            $mbox->host    = $route_addr['domain'];\r
+            $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';\r
+        } else {\r
+            $mbox->mailbox = $addr_spec['local_part'];\r
+            $mbox->host    = $addr_spec['domain'];\r
+        }\r
+\r
+        $mailbox = $mbox;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * This function validates a route-addr which is:\r
+     * route-addr = "<" [route] addr-spec ">"\r
+     *\r
+     * Angle brackets have already been removed at the point of\r
+     * getting to this function.\r
+     *\r
+     * @access private\r
+     * @param string $route_addr The string to check.\r
+     * @return mixed False on failure, or an array containing validated address/route information on success.\r
+     */\r
+    function _validateRouteAddr($route_addr)\r
+    {\r
+        // Check for colon.\r
+        if (strpos($route_addr, ':') !== false) {\r
+            $parts = explode(':', $route_addr);\r
+            $route = $this->_splitCheck($parts, ':');\r
+        } else {\r
+            $route = $route_addr;\r
+        }\r
+\r
+        // If $route is same as $route_addr then the colon was in\r
+        // quotes or brackets or, of course, non existent.\r
+        if ($route === $route_addr){\r
+            unset($route);\r
+            $addr_spec = $route_addr;\r
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
+                return false;\r
+            }\r
+        } else {\r
+            // Validate route part.\r
+            if (($route = $this->_validateRoute($route)) === false) {\r
+                return false;\r
+            }\r
+\r
+            $addr_spec = substr($route_addr, strlen($route . ':'));\r
+\r
+            // Validate addr-spec part.\r
+            if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {\r
+                return false;\r
+            }\r
+        }\r
+\r
+        if (isset($route)) {\r
+            $return['adl'] = $route;\r
+        } else {\r
+            $return['adl'] = '';\r
+        }\r
+\r
+        $return = array_merge($return, $addr_spec);\r
+        return $return;\r
+    }\r
+\r
+    /**\r
+     * Function to validate a route, which is:\r
+     * route = 1#("@" domain) ":"\r
+     *\r
+     * @access private\r
+     * @param string $route The string to check.\r
+     * @return mixed False on failure, or the validated $route on success.\r
+     */\r
+    function _validateRoute($route)\r
+    {\r
+        // Split on comma.\r
+        $domains = explode(',', trim($route));\r
+\r
+        foreach ($domains as $domain) {\r
+            $domain = str_replace('@', '', trim($domain));\r
+            if (!$this->_validateDomain($domain)) return false;\r
+        }\r
+\r
+        return $route;\r
+    }\r
+\r
+    /**\r
+     * Function to validate a domain, though this is not quite what\r
+     * you expect of a strict internet domain.\r
+     *\r
+     * domain = sub-domain *("." sub-domain)\r
+     *\r
+     * @access private\r
+     * @param string $domain The string to check.\r
+     * @return mixed False on failure, or the validated domain on success.\r
+     */\r
+    function _validateDomain($domain)\r
+    {\r
+        // Note the different use of $subdomains and $sub_domains\r
+        $subdomains = explode('.', $domain);\r
+\r
+        while (count($subdomains) > 0) {\r
+            $sub_domains[] = $this->_splitCheck($subdomains, '.');\r
+            for ($i = 0; $i < $this->index + 1; $i++)\r
+                array_shift($subdomains);\r
+        }\r
+\r
+        foreach ($sub_domains as $sub_domain) {\r
+            if (!$this->_validateSubdomain(trim($sub_domain)))\r
+                return false;\r
+        }\r
+\r
+        // Managed to get here, so return input.\r
+        return $domain;\r
+    }\r
+\r
+    /**\r
+     * Function to validate a subdomain:\r
+     *   subdomain = domain-ref / domain-literal\r
+     *\r
+     * @access private\r
+     * @param string $subdomain The string to check.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _validateSubdomain($subdomain)\r
+    {\r
+        if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){\r
+            if (!$this->_validateDliteral($arr[1])) return false;\r
+        } else {\r
+            if (!$this->_validateAtom($subdomain)) return false;\r
+        }\r
+\r
+        // Got here, so return successful.\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Function to validate a domain literal:\r
+     *   domain-literal =  "[" *(dtext / quoted-pair) "]"\r
+     *\r
+     * @access private\r
+     * @param string $dliteral The string to check.\r
+     * @return boolean Success or failure.\r
+     */\r
+    function _validateDliteral($dliteral)\r
+    {\r
+        return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';\r
+    }\r
+\r
+    /**\r
+     * Function to validate an addr-spec.\r
+     *\r
+     * addr-spec = local-part "@" domain\r
+     *\r
+     * @access private\r
+     * @param string $addr_spec The string to check.\r
+     * @return mixed False on failure, or the validated addr-spec on success.\r
+     */\r
+    function _validateAddrSpec($addr_spec)\r
+    {\r
+        $addr_spec = trim($addr_spec);\r
+\r
+        // Split on @ sign if there is one.\r
+        if (strpos($addr_spec, '@') !== false) {\r
+            $parts      = explode('@', $addr_spec);\r
+            $local_part = $this->_splitCheck($parts, '@');\r
+            $domain     = substr($addr_spec, strlen($local_part . '@'));\r
+\r
+        // No @ sign so assume the default domain.\r
+        } else {\r
+            $local_part = $addr_spec;\r
+            $domain     = $this->default_domain;\r
+        }\r
+\r
+        if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;\r
+        if (($domain     = $this->_validateDomain($domain)) === false) return false;\r
+\r
+        // Got here so return successful.\r
+        return array('local_part' => $local_part, 'domain' => $domain);\r
+    }\r
+\r
+    /**\r
+     * Function to validate the local part of an address:\r
+     *   local-part = word *("." word)\r
+     *\r
+     * @access private\r
+     * @param string $local_part\r
+     * @return mixed False on failure, or the validated local part on success.\r
+     */\r
+    function _validateLocalPart($local_part)\r
+    {\r
+        $parts = explode('.', $local_part);\r
+        $words = array();\r
+\r
+        // Split the local_part into words.\r
+        while (count($parts) > 0){\r
+            $words[] = $this->_splitCheck($parts, '.');\r
+            for ($i = 0; $i < $this->index + 1; $i++) {\r
+                array_shift($parts);\r
+            }\r
+        }\r
+\r
+        // Validate each word.\r
+        foreach ($words as $word) {\r
+            // If this word contains an unquoted space, it is invalid. (6.2.4)\r
+            if (strpos($word, ' ') && $word[0] !== '"')\r
+            {\r
+                return false;\r
+            }\r
+\r
+            if ($this->_validatePhrase(trim($word)) === false) return false;\r
+        }\r
+\r
+        // Managed to get here, so return the input.\r
+        return $local_part;\r
+    }\r
+\r
+    /**\r
+     * Returns an approximate count of how many addresses are in the\r
+     * given string. This is APPROXIMATE as it only splits based on a\r
+     * comma which has no preceding backslash. Could be useful as\r
+     * large amounts of addresses will end up producing *large*\r
+     * structures when used with parseAddressList().\r
+     *\r
+     * @param  string $data Addresses to count\r
+     * @return int          Approximate count\r
+     */\r
+    function approximateCount($data)\r
+    {\r
+        return count(preg_split('/(?<!\\\\),/', $data));\r
+    }\r
+\r
+    /**\r
+     * This is a email validating function separate to the rest of the\r
+     * class. It simply validates whether an email is of the common\r
+     * internet form: <user>@<domain>. This can be sufficient for most\r
+     * people. Optional stricter mode can be utilised which restricts\r
+     * mailbox characters allowed to alphanumeric, full stop, hyphen\r
+     * and underscore.\r
+     *\r
+     * @param  string  $data   Address to check\r
+     * @param  boolean $strict Optional stricter mode\r
+     * @return mixed           False if it fails, an indexed array\r
+     *                         username/domain if it matches\r
+     */\r
+    function isValidInetAddress($data, $strict = false)\r
+    {\r
+        $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,4})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,4})$/i';\r
+        if (preg_match($regex, trim($data), $matches)) {\r
+            return array($matches[1], $matches[2]);\r
+        } else {\r
+            return false;\r
+        }\r
+    }\r
+\r
+}\r
index 975e6b2..f14dbd9 100644 (file)
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-// +-----------------------------------------------------------------------+
-// | Copyright (c) 2002-2003  Richard Heyes                                |
-// | Copyright (c) 2003-2005  The PHP Group                                |
-// | All rights reserved.                                                  |
-// |                                                                       |
-// | Redistribution and use in source and binary forms, with or without    |
-// | modification, are permitted provided that the following conditions    |
-// | are met:                                                              |
-// |                                                                       |
-// | o Redistributions of source code must retain the above copyright      |
-// |   notice, this list of conditions and the following disclaimer.       |
-// | o Redistributions in binary form must reproduce the above copyright   |
-// |   notice, this list of conditions and the following disclaimer in the |
-// |   documentation and/or other materials provided with the distribution.|
-// | o The names of the authors may not be used to endorse or promote      |
-// |   products derived from this software without specific prior written  |
-// |   permission.                                                         |
-// |                                                                       |
-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
-// |                                                                       |
-// +-----------------------------------------------------------------------+
-// | Author: Richard Heyes <richard@phpguru.org>                           |
-// +-----------------------------------------------------------------------+
-
-require_once 'PEAR.php';
-
-/**
-*  +----------------------------- IMPORTANT ------------------------------+
-*  | Usage of this class compared to native php extensions such as        |
-*  | mailparse or imap, is slow and may be feature deficient. If available|
-*  | you are STRONGLY recommended to use the php extensions.              |
-*  +----------------------------------------------------------------------+
-*
-* Mime Decoding class
-*
-* This class will parse a raw mime email and return
-* the structure. Returned structure is similar to
-* that returned by imap_fetchstructure().
-*
-* USAGE: (assume $input is your raw email)
-*
-* $decode = new Mail_mimeDecode($input, "\r\n");
-* $structure = $decode->decode();
-* print_r($structure);
-*
-* Or statically:
-*
-* $params['input'] = $input;
-* $structure = Mail_mimeDecode::decode($params);
-* print_r($structure);
-*
-* TODO:
-*  o Implement multipart/appledouble
-*  o UTF8: ???
-
-               > 4. We have also found a solution for decoding the UTF-8 
-               > headers. Therefore I made the following function:
-               > 
-               > function decode_utf8($txt) {
-               > $trans=array("Å&#8216;"=>"õ","ű"=>"û","Å\90"=>"Ã&#8226;","Å°"
-               =>"Ã&#8250;");
-               > $txt=strtr($txt,$trans);
-               > return(utf8_decode($txt));
-               > }
-               > 
-               > And I have inserted the following line to the class:
-               > 
-               > if (strtolower($charset)=="utf-8") $text=decode_utf8($text);
-               > 
-               > ... before the following one in the "_decodeHeader" function:
-               > 
-               > $input = str_replace($encoded, $text, $input);
-               > 
-               > This way from now on it can easily decode the UTF-8 headers too.
-
-*
-* @author  Richard Heyes <richard@phpguru.org>
-* @version $Revision: 1.1 $
-* @package Mail
-*/
-class Mail_mimeDecode extends PEAR
-{
-    /**
-     * The raw email to decode
-     * @var    string
-     */
-    var $_input;
-
-    /**
-     * The header part of the input
-     * @var    string
-     */
-    var $_header;
-
-    /**
-     * The body part of the input
-     * @var    string
-     */
-    var $_body;
-
-    /**
-     * If an error occurs, this is used to store the message
-     * @var    string
-     */
-    var $_error;
-
-    /**
-     * Flag to determine whether to include bodies in the
-     * returned object.
-     * @var    boolean
-     */
-    var $_include_bodies;
-
-    /**
-     * Flag to determine whether to decode bodies
-     * @var    boolean
-     */
-    var $_decode_bodies;
-
-    /**
-     * Flag to determine whether to decode headers
-     * @var    boolean
-     */
-    var $_decode_headers;
-
-    /**
-     * Constructor.
-     *
-     * Sets up the object, initialise the variables, and splits and
-     * stores the header and body of the input.
-     *
-     * @param string The input to decode
-     * @access public
-     */
-    function Mail_mimeDecode($input)
-    {
-        list($header, $body)   = $this->_splitBodyHeader($input);
-
-        $this->_input          = $input;
-        $this->_header         = $header;
-        $this->_body           = $body;
-        $this->_decode_bodies  = false;
-        $this->_include_bodies = true;
-    }
-
-    /**
-     * Begins the decoding process. If called statically
-     * it will create an object and call the decode() method
-     * of it.
-     *
-     * @param array An array of various parameters that determine
-     *              various things:
-     *              include_bodies - Whether to include the body in the returned
-     *                               object.
-     *              decode_bodies  - Whether to decode the bodies
-     *                               of the parts. (Transfer encoding)
-     *              decode_headers - Whether to decode headers
-     *              input          - If called statically, this will be treated
-     *                               as the input
-     * @return object Decoded results
-     * @access public
-     */
-    function decode($params = null)
-    {
-        // determine if this method has been called statically
-        $isStatic = !(isset($this) && get_class($this) == __CLASS__);
-
-        // Have we been called statically?
-       // If so, create an object and pass details to that.
-        if ($isStatic AND isset($params['input'])) {
-
-            $obj = new Mail_mimeDecode($params['input']);
-            $structure = $obj->decode($params);
-
-        // Called statically but no input
-        } elseif ($isStatic) {
-            return PEAR::raiseError('Called statically and no input given');
-
-        // Called via an object
-        } else {
-            $this->_include_bodies = isset($params['include_bodies']) ?
-                                    $params['include_bodies'] : false;
-            $this->_decode_bodies  = isset($params['decode_bodies']) ?
-                                    $params['decode_bodies']  : false;
-            $this->_decode_headers = isset($params['decode_headers']) ?
-                                    $params['decode_headers'] : false;
-
-            $structure = $this->_decode($this->_header, $this->_body);
-            if ($structure === false) {
-                $structure = $this->raiseError($this->_error);
-            }
-        }
-
-        return $structure;
-    }
-
-    /**
-     * Performs the decoding. Decodes the body string passed to it
-     * If it finds certain content-types it will call itself in a
-     * recursive fashion
-     *
-     * @param string Header section
-     * @param string Body section
-     * @return object Results of decoding process
-     * @access private
-     */
-    function _decode($headers, $body, $default_ctype = 'text/plain')
-    {
-        $return = new stdClass;
-        $return->headers = array();
-        $headers = $this->_parseHeaders($headers);
-
-        foreach ($headers as $value) {
-            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
-                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);
-                $return->headers[strtolower($value['name'])][] = $value['value'];
-
-            } elseif (isset($return->headers[strtolower($value['name'])])) {
-                $return->headers[strtolower($value['name'])][] = $value['value'];
-
-            } else {
-                $return->headers[strtolower($value['name'])] = $value['value'];
-            }
-        }
-
-        reset($headers);
-        while (list($key, $value) = each($headers)) {
-            $headers[$key]['name'] = strtolower($headers[$key]['name']);
-            switch ($headers[$key]['name']) {
-
-                case 'content-type':
-                    $content_type = $this->_parseHeaderValue($headers[$key]['value']);
-
-                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
-                        $return->ctype_primary   = $regs[1];
-                        $return->ctype_secondary = $regs[2];
-                    }
-
-                    if (isset($content_type['other'])) {
-                        while (list($p_name, $p_value) = each($content_type['other'])) {
-                            $return->ctype_parameters[$p_name] = $p_value;
-                        }
-                    }
-                    break;
-
-                case 'content-disposition':
-                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
-                    $return->disposition   = $content_disposition['value'];
-                    if (isset($content_disposition['other'])) {
-                        while (list($p_name, $p_value) = each($content_disposition['other'])) {
-                            $return->d_parameters[$p_name] = $p_value;
-                        }
-                    }
-                    break;
-
-                case 'content-transfer-encoding':
-                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
-                    break;
-            }
-        }
-
-        if (isset($content_type)) {
-            switch (strtolower($content_type['value'])) {
-                case 'text/plain':
-                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
-                    break;
-
-                case 'text/html':
-                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
-                    break;
-
-                case 'multipart/parallel':
-                case 'multipart/report': // RFC1892
-                case 'multipart/signed': // PGP
-                case 'multipart/digest':
-                case 'multipart/alternative':
-                case 'multipart/related':
-                case 'multipart/mixed':
-                    if(!isset($content_type['other']['boundary'])){
-                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
-                        return false;
-                    }
-
-                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
-
-                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
-                    for ($i = 0; $i < count($parts); $i++) {
-                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
-                        $part = $this->_decode($part_header, $part_body, $default_ctype);
-                        if($part === false)
-                            $part = $this->raiseError($this->_error);
-                        $return->parts[] = $part;
-                    }
-                    break;
-
-                case 'message/rfc822':
-                    $obj = &new Mail_mimeDecode($body);
-                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
-                                                                             'decode_bodies'  => $this->_decode_bodies,
-                                                                                                                 'decode_headers' => $this->_decode_headers));
-                    unset($obj);
-                    break;
-
-                default:
-                    if(!isset($content_transfer_encoding['value']))
-                        $content_transfer_encoding['value'] = '7bit';
-                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
-                    break;
-            }
-
-        } else {
-            $ctype = explode('/', $default_ctype);
-            $return->ctype_primary   = $ctype[0];
-            $return->ctype_secondary = $ctype[1];
-            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
-        }
-
-        return $return;
-    }
-
-    /**
-     * Given the output of the above function, this will return an
-     * array of references to the parts, indexed by mime number.
-     *
-     * @param  object $structure   The structure to go through
-     * @param  string $mime_number Internal use only.
-     * @return array               Mime numbers
-     */
-    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
-    {
-        $return = array();
-        if (!empty($structure->parts)) {
-            if ($mime_number != '') {
-                $structure->mime_id = $prepend . $mime_number;
-                $return[$prepend . $mime_number] = &$structure;
-            }
-            for ($i = 0; $i < count($structure->parts); $i++) {
-
-            
-                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
-                    $prepend      = $prepend . $mime_number . '.';
-                    $_mime_number = '';
-                } else {
-                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
-                }
-
-                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
-                foreach ($arr as $key => $val) {
-                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
-                }
-            }
-        } else {
-            if ($mime_number == '') {
-                $mime_number = '1';
-            }
-            $structure->mime_id = $prepend . $mime_number;
-            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
-        }
-        
-        return $return;
-    }
-
-    /**
-     * Given a string containing a header and body
-     * section, this function will split them (at the first
-     * blank line) and return them.
-     *
-     * @param string Input to split apart
-     * @return array Contains header and body section
-     * @access private
-     */
-    function _splitBodyHeader($input)
-    {
-        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
-            return array($match[1], $match[2]);
-        }
-        $this->_error = 'Could not split header and body';
-        return false;
-    }
-
-    /**
-     * Parse headers given in $input and return
-     * as assoc array.
-     *
-     * @param string Headers to parse
-     * @return array Contains parsed headers
-     * @access private
-     */
-    function _parseHeaders($input)
-    {
-
-        if ($input !== '') {
-            // Unfold the input
-            $input   = preg_replace("/\r?\n/", "\r\n", $input);
-            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input);
-            $headers = explode("\r\n", trim($input));
-
-            foreach ($headers as $value) {
-                $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
-                $hdr_value = substr($value, $pos+1);
-                if($hdr_value[0] == ' ')
-                    $hdr_value = substr($hdr_value, 1);
-
-                $return[] = array(
-                                  'name'  => $hdr_name,
-                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
-                                 );
-            }
-        } else {
-            $return = array();
-        }
-
-        return $return;
-    }
-
-    /**
-     * Function to parse a header value,
-     * extract first part, and any secondary
-     * parts (after ;) This function is not as
-     * robust as it could be. Eg. header comments
-     * in the wrong place will probably break it.
-     *
-     * @param string Header value to parse
-     * @return array Contains parsed result
-     * @access private
-     */
-    function _parseHeaderValue($input)
-    {
-
-        if (($pos = strpos($input, ';')) !== false) {
-
-            $return['value'] = trim(substr($input, 0, $pos));
-            $input = trim(substr($input, $pos+1));
-
-            if (strlen($input) > 0) {
-
-                // This splits on a semi-colon, if there's no preceeding backslash
-                // Now works with quoted values; had to glue the \; breaks in PHP
-                // the regex is already bordering on incomprehensible
-                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
-                preg_match_all($splitRegex, $input, $matches);
-                $parameters = array();
-                for ($i=0; $i<count($matches[0]); $i++) {
-                    $param = $matches[0][$i];
-                    while (substr($param, -2) == '\;') {
-                        $param .= $matches[0][++$i];
-                    }
-                    $parameters[] = $param;
-                }
-
-                for ($i = 0; $i < count($parameters); $i++) {
-                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
-                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
-                    if ($param_value[0] == '"') {
-                        $param_value = substr($param_value, 1, -1);
-                    }
-                    $return['other'][$param_name] = $param_value;
-                    $return['other'][strtolower($param_name)] = $param_value;
-                }
-            }
-        } else {
-            $return['value'] = trim($input);
-        }
-
-        return $return;
-    }
-
-    /**
-     * This function splits the input based
-     * on the given boundary
-     *
-     * @param string Input to parse
-     * @return array Contains array of resulting mime parts
-     * @access private
-     */
-    function _boundarySplit($input, $boundary)
-    {
-        $parts = array();
-
-        $bs_possible = substr($boundary, 2, -2);
-        $bs_check = '\"' . $bs_possible . '\"';
-
-        if ($boundary == $bs_check) {
-            $boundary = $bs_possible;
-        }
-
-        $tmp = explode('--' . $boundary, $input);
-
-        for ($i = 1; $i < count($tmp) - 1; $i++) {
-            $parts[] = $tmp[$i];
-        }
-
-        return $parts;
-    }
-
-    /**
-     * Given a header, this function will decode it
-     * according to RFC2047. Probably not *exactly*
-     * conformant, but it does pass all the given
-     * examples (in RFC2047).
-     *
-     * @param string Input header value to decode
-     * @return string Decoded header value
-     * @access private
-     */
-    function _decodeHeader($input)
-    {
-        // Remove white space between encoded-words
-        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
-
-        // For each encoded-word...
-        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
-
-            $encoded  = $matches[1];
-            $charset  = $matches[2];
-            $encoding = $matches[3];
-            $text     = $matches[4];
-
-            switch (strtolower($encoding)) {
-                case 'b':
-                    $text = base64_decode($text);
-                    break;
-
-                case 'q':
-                    $text = str_replace('_', ' ', $text);
-                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
-                    foreach($matches[1] as $value)
-                        $text = str_replace('='.$value, chr(hexdec($value)), $text);
-                    break;
-            }
-
-            $input = str_replace($encoded, $text, $input);
-        }
-
-        return $input;
-    }
-
-    /**
-     * Given a body string and an encoding type,
-     * this function will decode and return it.
-     *
-     * @param  string Input body to decode
-     * @param  string Encoding type to use.
-     * @return string Decoded body
-     * @access private
-     */
-    function _decodeBody($input, $encoding = '7bit')
-    {
-        switch (strtolower($encoding)) {
-            case '7bit':
-                return $input;
-                break;
-
-            case 'quoted-printable':
-                return $this->_quotedPrintableDecode($input);
-                break;
-
-            case 'base64':
-                return base64_decode($input);
-                break;
-
-            default:
-                return $input;
-        }
-    }
-
-    /**
-     * Given a quoted-printable string, this
-     * function will decode and return it.
-     *
-     * @param  string Input body to decode
-     * @return string Decoded body
-     * @access private
-     */
-    function _quotedPrintableDecode($input)
-    {
-        // Remove soft line breaks
-        $input = preg_replace("/=\r?\n/", '', $input);
-
-        // Replace encoded characters
-               $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
-
-        return $input;
-    }
-
-    /**
-     * Checks the input for uuencoded files and returns
-     * an array of them. Can be called statically, eg:
-     *
-     * $files =& Mail_mimeDecode::uudecode($some_text);
-     *
-     * It will check for the begin 666 ... end syntax
-     * however and won't just blindly decode whatever you
-     * pass it.
-     *
-     * @param  string Input body to look for attahcments in
-     * @return array  Decoded bodies, filenames and permissions
-     * @access public
-     * @author Unknown
-     */
-    function &uudecode($input)
-    {
-        // Find all uuencoded sections
-        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
-
-        for ($j = 0; $j < count($matches[3]); $j++) {
-
-            $str      = $matches[3][$j];
-            $filename = $matches[2][$j];
-            $fileperm = $matches[1][$j];
-
-            $file = '';
-            $str = preg_split("/\r?\n/", trim($str));
-            $strlen = count($str);
-
-            for ($i = 0; $i < $strlen; $i++) {
-                $pos = 1;
-                $d = 0;
-                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
-
-                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
-                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
-
-                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077));
-
-                    $pos += 4;
-                    $d += 3;
-                }
-
-                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
-
-                    $pos += 3;
-                    $d += 2;
-                }
-
-                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
-                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
-                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
-                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
-
-                }
-            }
-            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
-        }
-
-        return $files;
-    }
-
-    /**
-     * getSendArray() returns the arguments required for Mail::send()
-     * used to build the arguments for a mail::send() call 
-     *
-     * Usage:
-     * $mailtext = Full email (for example generated by a template)
-     * $decoder = new Mail_mimeDecode($mailtext);
-     * $parts =  $decoder->getSendArray();
-     * if (!PEAR::isError($parts) {
-     *     list($recipents,$headers,$body) = $parts;
-     *     $mail = Mail::factory('smtp');
-     *     $mail->send($recipents,$headers,$body);
-     * } else {
-     *     echo $parts->message;
-     * }
-     * @return mixed   array of recipeint, headers,body or Pear_Error
-     * @access public
-     * @author Alan Knowles <alan@akbkhome.com>
-     */
-    function getSendArray()
-    {
-        // prevent warning if this is not set
-        $this->_decode_headers = FALSE;
-        $headerlist =$this->_parseHeaders($this->_header);
-        $to = "";
-        if (!$headerlist) {
-            return $this->raiseError("Message did not contain headers");
-        }
-        foreach($headerlist as $item) {
-            $header[$item['name']] = $item['value'];
-            switch (strtolower($item['name'])) {
-                case "to":
-                case "cc":
-                case "bcc":
-                    $to = ",".$item['value'];
-                default:
-                   break;
-            }
-        }
-        if ($to == "") {
-            return $this->raiseError("Message did not contain any recipents");
-        }
-        $to = substr($to,1);
-        return array($to,$header,$this->_body);
-    } 
-
-    /**
-     * Returns a xml copy of the output of
-     * Mail_mimeDecode::decode. Pass the output in as the
-     * argument. This function can be called statically. Eg:
-     *
-     * $output = $obj->decode();
-     * $xml    = Mail_mimeDecode::getXML($output);
-     *
-     * The DTD used for this should have been in the package. Or
-     * alternatively you can get it from cvs, or here:
-     * http://www.phpguru.org/xmail/xmail.dtd.
-     *
-     * @param  object Input to convert to xml. This should be the
-     *                output of the Mail_mimeDecode::decode function
-     * @return string XML version of input
-     * @access public
-     */
-    function getXML($input)
-    {
-        $crlf    =  "\r\n";
-        $output  = '<?xml version=\'1.0\'?>' . $crlf .
-                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
-                   '<email>' . $crlf .
-                   Mail_mimeDecode::_getXML($input) .
-                   '</email>';
-
-        return $output;
-    }
-
-    /**
-     * Function that does the actual conversion to xml. Does a single
-     * mimepart at a time.
-     *
-     * @param  object  Input to convert to xml. This is a mimepart object.
-     *                 It may or may not contain subparts.
-     * @param  integer Number of tabs to indent
-     * @return string  XML version of input
-     * @access private
-     */
-    function _getXML($input, $indent = 1)
-    {
-        $htab    =  "\t";
-        $crlf    =  "\r\n";
-        $output  =  '';
-        $headers = @(array)$input->headers;
-
-        foreach ($headers as $hdr_name => $hdr_value) {
-
-            // Multiple headers with this name
-            if (is_array($headers[$hdr_name])) {
-                for ($i = 0; $i < count($hdr_value); $i++) {
-                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
-                }
-
-            // Only one header of this sort
-            } else {
-                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
-            }
-        }
-
-        if (!empty($input->parts)) {
-            for ($i = 0; $i < count($input->parts); $i++) {
-                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
-                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
-                           str_repeat($htab, $indent) . '</mimepart>' . $crlf;
-            }
-        } elseif (isset($input->body)) {
-            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
-                       $input->body . ']]></body>' . $crlf;
-        }
-
-        return $output;
-    }
-
-    /**
-     * Helper function to _getXML(). Returns xml of a header.
-     *
-     * @param  string  Name of header
-     * @param  string  Value of header
-     * @param  integer Number of tabs to indent
-     * @return string  XML version of input
-     * @access private
-     */
-    function _getXML_helper($hdr_name, $hdr_value, $indent)
-    {
-        $htab   = "\t";
-        $crlf   = "\r\n";
-        $return = '';
-
-        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
-        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
-
-        // Sort out any parameters
-        if (!empty($new_hdr_value['other'])) {
-            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
-                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
-                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
-            }
-
-            $params = implode('', $params);
-        } else {
-            $params = '';
-        }
-
-        $return = str_repeat($htab, $indent) . '<header>' . $crlf .
-                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
-                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
-                  $params .
-                  str_repeat($htab, $indent) . '</header>' . $crlf;
-
-        return $return;
-    }
-
-} // End of class
-?>
+<?php\r
+/**\r
+ * The Mail_mimeDecode class is used to decode mail/mime messages\r
+ *\r
+ * This class will parse a raw mime email and return\r
+ * the structure. Returned structure is similar to\r
+ * that returned by imap_fetchstructure().\r
+ *\r
+ *  +----------------------------- IMPORTANT ------------------------------+\r
+ *  | Usage of this class compared to native php extensions such as        |\r
+ *  | mailparse or imap, is slow and may be feature deficient. If available|\r
+ *  | you are STRONGLY recommended to use the php extensions.              |\r
+ *  +----------------------------------------------------------------------+\r
+ *\r
+ * Compatible with PHP versions 4 and 5\r
+ *\r
+ * LICENSE: This LICENSE is in the BSD license style.\r
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>\r
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright\r
+ *   notice, this list of conditions and the following disclaimer.\r
+ * - Redistributions in binary form must reproduce the above copyright\r
+ *   notice, this list of conditions and the following disclaimer in the\r
+ *   documentation and/or other materials provided with the distribution.\r
+ * - Neither the name of the authors, nor the names of its contributors \r
+ *   may be used to endorse or promote products derived from this \r
+ *   software without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\r
+ * THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * @category   Mail\r
+ * @package    Mail_Mime\r
+ * @author     Richard Heyes  <richard@phpguru.org>\r
+ * @author     George Schlossnagle <george@omniti.com>\r
+ * @author     Cipriano Groenendal <cipri@php.net>\r
+ * @author     Sean Coates <sean@php.net>\r
+ * @copyright  2003-2006 PEAR <pear-group@php.net>\r
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License\r
+ * @version    CVS: $Id: mimeDecode.php,v 1.8 2008/07/19 17:46:36 hsur Exp $\r
+ * @link       http://pear.php.net/package/Mail_mime\r
+ */\r
+\r
+\r
+/**\r
+ * require PEAR\r
+ *\r
+ * This package depends on PEAR to raise errors.\r
+ */\r
+require_once 'PEAR.php';\r
+\r
+\r
+/**\r
+ * The Mail_mimeDecode class is used to decode mail/mime messages\r
+ *\r
+ * This class will parse a raw mime email and return the structure.\r
+ * Returned structure is similar to that returned by imap_fetchstructure().\r
+ *\r
+ *  +----------------------------- IMPORTANT ------------------------------+\r
+ *  | Usage of this class compared to native php extensions such as        |\r
+ *  | mailparse or imap, is slow and may be feature deficient. If available|\r
+ *  | you are STRONGLY recommended to use the php extensions.              |\r
+ *  +----------------------------------------------------------------------+\r
+ *\r
+ * @category   Mail\r
+ * @package    Mail_Mime\r
+ * @author     Richard Heyes  <richard@phpguru.org>\r
+ * @author     George Schlossnagle <george@omniti.com>\r
+ * @author     Cipriano Groenendal <cipri@php.net>\r
+ * @author     Sean Coates <sean@php.net>\r
+ * @copyright  2003-2006 PEAR <pear-group@php.net>\r
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License\r
+ * @version    Release: @package_version@\r
+ * @link       http://pear.php.net/package/Mail_mime\r
+ */\r
+class Mail_mimeDecode extends PEAR\r
+{\r
+    /**\r
+     * The raw email to decode\r
+     *\r
+     * @var    string\r
+     * @access private\r
+     */\r
+    var $_input;\r
+\r
+    /**\r
+     * The header part of the input\r
+     *\r
+     * @var    string\r
+     * @access private\r
+     */\r
+    var $_header;\r
+\r
+    /**\r
+     * The body part of the input\r
+     *\r
+     * @var    string\r
+     * @access private\r
+     */\r
+    var $_body;\r
+\r
+    /**\r
+     * If an error occurs, this is used to store the message\r
+     *\r
+     * @var    string\r
+     * @access private\r
+     */\r
+    var $_error;\r
+\r
+    /**\r
+     * Flag to determine whether to include bodies in the\r
+     * returned object.\r
+     *\r
+     * @var    boolean\r
+     * @access private\r
+     */\r
+    var $_include_bodies;\r
+\r
+    /**\r
+     * Flag to determine whether to decode bodies\r
+     *\r
+     * @var    boolean\r
+     * @access private\r
+     */\r
+    var $_decode_bodies;\r
+\r
+    /**\r
+     * Flag to determine whether to decode headers\r
+     *\r
+     * @var    boolean\r
+     * @access private\r
+     */\r
+    var $_decode_headers;\r
+\r
+    /**\r
+     * Constructor.\r
+     *\r
+     * Sets up the object, initialise the variables, and splits and\r
+     * stores the header and body of the input.\r
+     *\r
+     * @param string The input to decode\r
+     * @access public\r
+     */\r
+    function Mail_mimeDecode($input)\r
+    {\r
+        list($header, $body)   = $this->_splitBodyHeader($input);\r
+\r
+        $this->_input          = $input;\r
+        $this->_header         = $header;\r
+        $this->_body           = $body;\r
+        $this->_decode_bodies  = false;\r
+        $this->_include_bodies = true;\r
+    }\r
+\r
+    /**\r
+     * Begins the decoding process. If called statically\r
+     * it will create an object and call the decode() method\r
+     * of it.\r
+     *\r
+     * @param array An array of various parameters that determine\r
+     *              various things:\r
+     *              include_bodies - Whether to include the body in the returned\r
+     *                               object.\r
+     *              decode_bodies  - Whether to decode the bodies\r
+     *                               of the parts. (Transfer encoding)\r
+     *              decode_headers - Whether to decode headers\r
+     *              input          - If called statically, this will be treated\r
+     *                               as the input\r
+     * @return object Decoded results\r
+     * @access public\r
+     */\r
+    function decode($params = null)\r
+    {\r
+        // determine if this method has been called statically\r
+        $isStatic = !(isset($this) && get_class($this) == __CLASS__);\r
+\r
+        // Have we been called statically?\r
+       // If so, create an object and pass details to that.\r
+        if ($isStatic AND isset($params['input'])) {\r
+\r
+            $obj = new Mail_mimeDecode($params['input']);\r
+            $structure = $obj->decode($params);\r
+\r
+        // Called statically but no input\r
+        } elseif ($isStatic) {\r
+            return PEAR::raiseError('Called statically and no input given');\r
+\r
+        // Called via an object\r
+        } else {\r
+            $this->_include_bodies = isset($params['include_bodies']) ?\r
+                                    $params['include_bodies'] : false;\r
+            $this->_decode_bodies  = isset($params['decode_bodies']) ?\r
+                                    $params['decode_bodies']  : false;\r
+            $this->_decode_headers = isset($params['decode_headers']) ?\r
+                                    $params['decode_headers'] : false;\r
+\r
+            $structure = $this->_decode($this->_header, $this->_body);\r
+            if ($structure === false) {\r
+                $structure = $this->raiseError($this->_error);\r
+            }\r
+        }\r
+\r
+        return $structure;\r
+    }\r
+\r
+    /**\r
+     * Performs the decoding. Decodes the body string passed to it\r
+     * If it finds certain content-types it will call itself in a\r
+     * recursive fashion\r
+     *\r
+     * @param string Header section\r
+     * @param string Body section\r
+     * @return object Results of decoding process\r
+     * @access private\r
+     */\r
+    function _decode($headers, $body, $default_ctype = 'text/plain')\r
+    {\r
+        $return = new stdClass;\r
+        $return->headers = array();\r
+        $headers = $this->_parseHeaders($headers);\r
+\r
+        foreach ($headers as $value) {\r
+            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {\r
+                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);\r
+                $return->headers[strtolower($value['name'])][] = $value['value'];\r
+\r
+            } elseif (isset($return->headers[strtolower($value['name'])])) {\r
+                $return->headers[strtolower($value['name'])][] = $value['value'];\r
+\r
+            } else {\r
+                $return->headers[strtolower($value['name'])] = $value['value'];\r
+            }\r
+        }\r
+\r
+        reset($headers);\r
+        while (list($key, $value) = each($headers)) {\r
+            $headers[$key]['name'] = strtolower($headers[$key]['name']);\r
+            switch ($headers[$key]['name']) {\r
+\r
+                case 'content-type':\r
+                    $content_type = $this->_parseHeaderValue($headers[$key]['value']);\r
+\r
+                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {\r
+                        $return->ctype_primary   = $regs[1];\r
+                        $return->ctype_secondary = $regs[2];\r
+                    }\r
+\r
+                    if (isset($content_type['other'])) {\r
+                        while (list($p_name, $p_value) = each($content_type['other'])) {\r
+                            $return->ctype_parameters[$p_name] = $p_value;\r
+                        }\r
+                    }\r
+                    break;\r
+\r
+                case 'content-disposition':\r
+                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);\r
+                    $return->disposition   = $content_disposition['value'];\r
+                    if (isset($content_disposition['other'])) {\r
+                        while (list($p_name, $p_value) = each($content_disposition['other'])) {\r
+                            $return->d_parameters[$p_name] = $p_value;\r
+                        }\r
+                    }\r
+                    break;\r
+\r
+                case 'content-transfer-encoding':\r
+                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);\r
+                    break;\r
+            }\r
+        }\r
+\r
+        if (isset($content_type)) {\r
+            switch (strtolower($content_type['value'])) {\r
+                case 'text/plain':\r
+                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';\r
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;\r
+                    break;\r
+\r
+                case 'text/html':\r
+                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';\r
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;\r
+                    break;\r
+\r
+                case 'multipart/parallel':\r
+                case 'multipart/appledouble': // Appledouble mail\r
+                case 'multipart/report': // RFC1892\r
+                case 'multipart/signed': // PGP\r
+                case 'multipart/digest':\r
+                case 'multipart/alternative':\r
+                case 'multipart/related':\r
+                case 'multipart/mixed':\r
+                    if(!isset($content_type['other']['boundary'])){\r
+                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';\r
+                        return false;\r
+                    }\r
+\r
+                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';\r
+\r
+                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);\r
+                    for ($i = 0; $i < count($parts); $i++) {\r
+                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);\r
+                        $part = $this->_decode($part_header, $part_body, $default_ctype);\r
+                        if($part === false)\r
+                            $part = $this->raiseError($this->_error);\r
+                        $return->parts[] = $part;\r
+                    }\r
+                    break;\r
+\r
+                case 'message/rfc822':\r
+                    $obj = &new Mail_mimeDecode($body);\r
+                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,\r
+                                                                             'decode_bodies'  => $this->_decode_bodies,\r
+                                                                                                                 'decode_headers' => $this->_decode_headers));\r
+                    unset($obj);\r
+                    break;\r
+\r
+                default:\r
+                    if(!isset($content_transfer_encoding['value']))\r
+                        $content_transfer_encoding['value'] = '7bit';\r
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;\r
+                    break;\r
+            }\r
+\r
+        } else {\r
+            $ctype = explode('/', $default_ctype);\r
+            $return->ctype_primary   = $ctype[0];\r
+            $return->ctype_secondary = $ctype[1];\r
+            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;\r
+        }\r
+\r
+        return $return;\r
+    }\r
+\r
+    /**\r
+     * Given the output of the above function, this will return an\r
+     * array of references to the parts, indexed by mime number.\r
+     *\r
+     * @param  object $structure   The structure to go through\r
+     * @param  string $mime_number Internal use only.\r
+     * @return array               Mime numbers\r
+     */\r
+    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')\r
+    {\r
+        $return = array();\r
+        if (!empty($structure->parts)) {\r
+            if ($mime_number != '') {\r
+                $structure->mime_id = $prepend . $mime_number;\r
+                $return[$prepend . $mime_number] = &$structure;\r
+            }\r
+            for ($i = 0; $i < count($structure->parts); $i++) {\r
+\r
+            \r
+                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {\r
+                    $prepend      = $prepend . $mime_number . '.';\r
+                    $_mime_number = '';\r
+                } else {\r
+                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));\r
+                }\r
+\r
+                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);\r
+                foreach ($arr as $key => $val) {\r
+                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];\r
+                }\r
+            }\r
+        } else {\r
+            if ($mime_number == '') {\r
+                $mime_number = '1';\r
+            }\r
+            $structure->mime_id = $prepend . $mime_number;\r
+            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;\r
+        }\r
+        \r
+        return $return;\r
+    }\r
+\r
+    /**\r
+     * Given a string containing a header and body\r
+     * section, this function will split them (at the first\r
+     * blank line) and return them.\r
+     *\r
+     * @param string Input to split apart\r
+     * @return array Contains header and body section\r
+     * @access private\r
+     */\r
+    function _splitBodyHeader($input)\r
+    {\r
+        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {\r
+            return array($match[1], $match[2]);\r
+        }\r
+        $this->_error = 'Could not split header and body';\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Parse headers given in $input and return\r
+     * as assoc array.\r
+     *\r
+     * @param string Headers to parse\r
+     * @return array Contains parsed headers\r
+     * @access private\r
+     */\r
+    function _parseHeaders($input)\r
+    {\r
+\r
+        if ($input !== '') {\r
+            // Unfold the input\r
+            $input   = preg_replace("/\r?\n/", "\r\n", $input);\r
+            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input);\r
+            $headers = explode("\r\n", trim($input));\r
+\r
+            foreach ($headers as $value) {\r
+                $hdr_name = substr($value, 0, $pos = strpos($value, ':'));\r
+                $hdr_value = substr($value, $pos+1);\r
+                if($hdr_value[0] == ' ')\r
+                    $hdr_value = substr($hdr_value, 1);\r
+\r
+                $return[] = array(\r
+                                  'name'  => $hdr_name,\r
+                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value\r
+                                 );\r
+            }\r
+        } else {\r
+            $return = array();\r
+        }\r
+\r
+        return $return;\r
+    }\r
+\r
+    /**\r
+     * Function to parse a header value,\r
+     * extract first part, and any secondary\r
+     * parts (after ;) This function is not as\r
+     * robust as it could be. Eg. header comments\r
+     * in the wrong place will probably break it.\r
+     *\r
+     * @param string Header value to parse\r
+     * @return array Contains parsed result\r
+     * @access private\r
+     */\r
+    function _parseHeaderValue($input)\r
+    {\r
+\r
+        if (($pos = strpos($input, ';')) !== false) {\r
+\r
+            $return['value'] = trim(substr($input, 0, $pos));\r
+            $input = trim(substr($input, $pos+1));\r
+\r
+            if (strlen($input) > 0) {\r
+\r
+                // This splits on a semi-colon, if there's no preceeding backslash\r
+                // Now works with quoted values; had to glue the \; breaks in PHP\r
+                // the regex is already bordering on incomprehensible\r
+                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';\r
+                preg_match_all($splitRegex, $input, $matches);\r
+                $parameters = array();\r
+                for ($i=0; $i<count($matches[0]); $i++) {\r
+                    $param = $matches[0][$i];\r
+                    while (substr($param, -2) == '\;') {\r
+                        $param .= $matches[0][++$i];\r
+                    }\r
+                    $parameters[] = $param;\r
+                }\r
+\r
+                for ($i = 0; $i < count($parameters); $i++) {\r
+                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");\r
+                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");\r
+                    if ($param_value[0] == '"') {\r
+                        $param_value = substr($param_value, 1, -1);\r
+                    }\r
+                    $return['other'][$param_name] = $param_value;\r
+                    $return['other'][strtolower($param_name)] = $param_value;\r
+                }\r
+            }\r
+        } else {\r
+            $return['value'] = trim($input);\r
+        }\r
+\r
+        return $return;\r
+    }\r
+\r
+    /**\r
+     * This function splits the input based\r
+     * on the given boundary\r
+     *\r
+     * @param string Input to parse\r
+     * @return array Contains array of resulting mime parts\r
+     * @access private\r
+     */\r
+    function _boundarySplit($input, $boundary)\r
+    {\r
+        $parts = array();\r
+\r
+        $bs_possible = substr($boundary, 2, -2);\r
+        $bs_check = '\"' . $bs_possible . '\"';\r
+\r
+        if ($boundary == $bs_check) {\r
+            $boundary = $bs_possible;\r
+        }\r
+\r
+        $tmp = explode('--' . $boundary, $input);\r
+\r
+        for ($i = 1; $i < count($tmp) - 1; $i++) {\r
+            $parts[] = $tmp[$i];\r
+        }\r
+\r
+        return $parts;\r
+    }\r
+\r
+    /**\r
+     * Given a header, this function will decode it\r
+     * according to RFC2047. Probably not *exactly*\r
+     * conformant, but it does pass all the given\r
+     * examples (in RFC2047).\r
+     *\r
+     * @param string Input header value to decode\r
+     * @return string Decoded header value\r
+     * @access private\r
+     */\r
+    function _decodeHeader($input)\r
+    {\r
+        // Remove white space between encoded-words\r
+        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);\r
+\r
+        // For each encoded-word...\r
+        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {\r
+\r
+            $encoded  = $matches[1];\r
+            $charset  = $matches[2];\r
+            $encoding = $matches[3];\r
+            $text     = $matches[4];\r
+\r
+            switch (strtolower($encoding)) {\r
+                case 'b':\r
+                    $text = base64_decode($text);\r
+                    break;\r
+\r
+                case 'q':\r
+                    $text = str_replace('_', ' ', $text);\r
+                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);\r
+                    foreach($matches[1] as $value)\r
+                        $text = str_replace('='.$value, chr(hexdec($value)), $text);\r
+                    break;\r
+            }\r
+\r
+            $input = str_replace($encoded, $text, $input);\r
+        }\r
+\r
+        return $input;\r
+    }\r
+\r
+    /**\r
+     * Given a body string and an encoding type,\r
+     * this function will decode and return it.\r
+     *\r
+     * @param  string Input body to decode\r
+     * @param  string Encoding type to use.\r
+     * @return string Decoded body\r
+     * @access private\r
+     */\r
+    function _decodeBody($input, $encoding = '7bit')\r
+    {\r
+        switch (strtolower($encoding)) {\r
+            case '7bit':\r
+                return $input;\r
+                break;\r
+\r
+            case 'quoted-printable':\r
+                return $this->_quotedPrintableDecode($input);\r
+                break;\r
+\r
+            case 'base64':\r
+                return base64_decode($input);\r
+                break;\r
+\r
+            default:\r
+                return $input;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Given a quoted-printable string, this\r
+     * function will decode and return it.\r
+     *\r
+     * @param  string Input body to decode\r
+     * @return string Decoded body\r
+     * @access private\r
+     */\r
+    function _quotedPrintableDecode($input)\r
+    {\r
+        // Remove soft line breaks\r
+        $input = preg_replace("/=\r?\n/", '', $input);\r
+\r
+        // Replace encoded characters\r
+               $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);\r
+\r
+        return $input;\r
+    }\r
+\r
+    /**\r
+     * Checks the input for uuencoded files and returns\r
+     * an array of them. Can be called statically, eg:\r
+     *\r
+     * $files =& Mail_mimeDecode::uudecode($some_text);\r
+     *\r
+     * It will check for the begin 666 ... end syntax\r
+     * however and won't just blindly decode whatever you\r
+     * pass it.\r
+     *\r
+     * @param  string Input body to look for attahcments in\r
+     * @return array  Decoded bodies, filenames and permissions\r
+     * @access public\r
+     * @author Unknown\r
+     */\r
+    function &uudecode($input)\r
+    {\r
+        // Find all uuencoded sections\r
+        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);\r
+\r
+        for ($j = 0; $j < count($matches[3]); $j++) {\r
+\r
+            $str      = $matches[3][$j];\r
+            $filename = $matches[2][$j];\r
+            $fileperm = $matches[1][$j];\r
+\r
+            $file = '';\r
+            $str = preg_split("/\r?\n/", trim($str));\r
+            $strlen = count($str);\r
+\r
+            for ($i = 0; $i < $strlen; $i++) {\r
+                $pos = 1;\r
+                $d = 0;\r
+                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);\r
+\r
+                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {\r
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);\r
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);\r
+                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);\r
+                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);\r
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));\r
+\r
+                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));\r
+\r
+                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077));\r
+\r
+                    $pos += 4;\r
+                    $d += 3;\r
+                }\r
+\r
+                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {\r
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);\r
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);\r
+                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);\r
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));\r
+\r
+                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));\r
+\r
+                    $pos += 3;\r
+                    $d += 2;\r
+                }\r
+\r
+                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {\r
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);\r
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);\r
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));\r
+\r
+                }\r
+            }\r
+            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);\r
+        }\r
+\r
+        return $files;\r
+    }\r
+\r
+    /**\r
+     * getSendArray() returns the arguments required for Mail::send()\r
+     * used to build the arguments for a mail::send() call \r
+     *\r
+     * Usage:\r
+     * $mailtext = Full email (for example generated by a template)\r
+     * $decoder = new Mail_mimeDecode($mailtext);\r
+     * $parts =  $decoder->getSendArray();\r
+     * if (!PEAR::isError($parts) {\r
+     *     list($recipents,$headers,$body) = $parts;\r
+     *     $mail = Mail::factory('smtp');\r
+     *     $mail->send($recipents,$headers,$body);\r
+     * } else {\r
+     *     echo $parts->message;\r
+     * }\r
+     * @return mixed   array of recipeint, headers,body or Pear_Error\r
+     * @access public\r
+     * @author Alan Knowles <alan@akbkhome.com>\r
+     */\r
+    function getSendArray()\r
+    {\r
+        // prevent warning if this is not set\r
+        $this->_decode_headers = FALSE;\r
+        $headerlist =$this->_parseHeaders($this->_header);\r
+        $to = "";\r
+        if (!$headerlist) {\r
+            return $this->raiseError("Message did not contain headers");\r
+        }\r
+        foreach($headerlist as $item) {\r
+            $header[$item['name']] = $item['value'];\r
+            switch (strtolower($item['name'])) {\r
+                case "to":\r
+                case "cc":\r
+                case "bcc":\r
+                    $to = ",".$item['value'];\r
+                default:\r
+                   break;\r
+            }\r
+        }\r
+        if ($to == "") {\r
+            return $this->raiseError("Message did not contain any recipents");\r
+        }\r
+        $to = substr($to,1);\r
+        return array($to,$header,$this->_body);\r
+    } \r
+\r
+    /**\r
+     * Returns a xml copy of the output of\r
+     * Mail_mimeDecode::decode. Pass the output in as the\r
+     * argument. This function can be called statically. Eg:\r
+     *\r
+     * $output = $obj->decode();\r
+     * $xml    = Mail_mimeDecode::getXML($output);\r
+     *\r
+     * The DTD used for this should have been in the package. Or\r
+     * alternatively you can get it from cvs, or here:\r
+     * http://www.phpguru.org/xmail/xmail.dtd.\r
+     *\r
+     * @param  object Input to convert to xml. This should be the\r
+     *                output of the Mail_mimeDecode::decode function\r
+     * @return string XML version of input\r
+     * @access public\r
+     */\r
+    function getXML($input)\r
+    {\r
+        $crlf    =  "\r\n";\r
+        $output  = '<?xml version=\'1.0\'?>' . $crlf .\r
+                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .\r
+                   '<email>' . $crlf .\r
+                   Mail_mimeDecode::_getXML($input) .\r
+                   '</email>';\r
+\r
+        return $output;\r
+    }\r
+\r
+    /**\r
+     * Function that does the actual conversion to xml. Does a single\r
+     * mimepart at a time.\r
+     *\r
+     * @param  object  Input to convert to xml. This is a mimepart object.\r
+     *                 It may or may not contain subparts.\r
+     * @param  integer Number of tabs to indent\r
+     * @return string  XML version of input\r
+     * @access private\r
+     */\r
+    function _getXML($input, $indent = 1)\r
+    {\r
+        $htab    =  "\t";\r
+        $crlf    =  "\r\n";\r
+        $output  =  '';\r
+        $headers = @(array)$input->headers;\r
+\r
+        foreach ($headers as $hdr_name => $hdr_value) {\r
+\r
+            // Multiple headers with this name\r
+            if (is_array($headers[$hdr_name])) {\r
+                for ($i = 0; $i < count($hdr_value); $i++) {\r
+                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);\r
+                }\r
+\r
+            // Only one header of this sort\r
+            } else {\r
+                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);\r
+            }\r
+        }\r
+\r
+        if (!empty($input->parts)) {\r
+            for ($i = 0; $i < count($input->parts); $i++) {\r
+                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .\r
+                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .\r
+                           str_repeat($htab, $indent) . '</mimepart>' . $crlf;\r
+            }\r
+        } elseif (isset($input->body)) {\r
+            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .\r
+                       $input->body . ']]></body>' . $crlf;\r
+        }\r
+\r
+        return $output;\r
+    }\r
+\r
+    /**\r
+     * Helper function to _getXML(). Returns xml of a header.\r
+     *\r
+     * @param  string  Name of header\r
+     * @param  string  Value of header\r
+     * @param  integer Number of tabs to indent\r
+     * @return string  XML version of input\r
+     * @access private\r
+     */\r
+    function _getXML_helper($hdr_name, $hdr_value, $indent)\r
+    {\r
+        $htab   = "\t";\r
+        $crlf   = "\r\n";\r
+        $return = '';\r
+\r
+        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);\r
+        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));\r
+\r
+        // Sort out any parameters\r
+        if (!empty($new_hdr_value['other'])) {\r
+            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {\r
+                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .\r
+                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .\r
+                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .\r
+                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;\r
+            }\r
+\r
+            $params = implode('', $params);\r
+        } else {\r
+            $params = '';\r
+        }\r
+\r
+        $return = str_repeat($htab, $indent) . '<header>' . $crlf .\r
+                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .\r
+                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .\r
+                  $params .\r
+                  str_repeat($htab, $indent) . '</header>' . $crlf;\r
+\r
+        return $return;\r
+    }\r
+\r
+} // End of class\r
index fa71070..e574bc5 100644 (file)
-<?php
-// +-----------------------------------------------------------------------+
-// | Copyright (c) 2002, Richard Heyes                                     |
-// | All rights reserved.                                                  |
-// |                                                                       |
-// | Redistribution and use in source and binary forms, with or without    |
-// | modification, are permitted provided that the following conditions    |
-// | are met:                                                              |
-// |                                                                       |
-// | o Redistributions of source code must retain the above copyright      |
-// |   notice, this list of conditions and the following disclaimer.       |
-// | o Redistributions in binary form must reproduce the above copyright   |
-// |   notice, this list of conditions and the following disclaimer in the |
-// |   documentation and/or other materials provided with the distribution.|
-// | o The names of the authors may not be used to endorse or promote      |
-// |   products derived from this software without specific prior written  |
-// |   permission.                                                         |
-// |                                                                       |
-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
-// |                                                                       |
-// +-----------------------------------------------------------------------+
-// | Author: Richard Heyes <richard@phpguru.org>                           |
-// | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar>               |
-// +-----------------------------------------------------------------------+
-//
-// $Id: POP3.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-
-require_once('Net/Socket.php');
-
-
-
-/**
-*  +----------------------------- IMPORTANT ------------------------------+
-*  | Usage of this class compared to native php extensions such as IMAP   |
-*  | is slow and may be feature deficient. If available you are STRONGLY  |
-*  | recommended to use the php extensions.                               |
-*  +----------------------------------------------------------------------+
-*
-* POP3 Access Class
-*
-* For usage see the example script
-*/
-
-define('NET_POP3_STATE_DISCONNECTED',  1, true);
-define('NET_POP3_STATE_AUTHORISATION', 2, true);
-define('NET_POP3_STATE_TRANSACTION',   4, true);
-
-class Net_POP3 {
-
-    /*
-    * Some basic information about the mail drop
-    * garnered from the STAT command
-    *
-    * @var array
-    */
-    var $_maildrop;
-
-    /*
-    * Used for APOP to store the timestamp
-    *
-    * @var string
-    */
-    var $_timestamp;
-
-    /*
-    * Timeout that is passed to the socket object
-    *
-    * @var integer
-    */
-    var $_timeout;
-
-    /*
-    * Socket object
-    *
-    * @var object
-    */
-    var $_socket;
-
-    /*
-    * Current state of the connection. Used with the
-    * constants defined above.
-    *
-    * @var integer
-    */
-    var $_state;
-
-    /*
-    * Hostname to connect to
-    *
-    * @var string
-    */
-    var $_host;
-
-    /*
-    * Port to connect to
-    *
-    * @var integer
-    */
-    var $_port;
-
-    /**
-    * To allow class debuging
-    * @var boolean
-    */
-    var $_debug = false;
-
-
-    /**
-    * The auth methods this class support
-    * @var array
-    */
-    //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
-    //Disabling DIGEST-MD5 for now
-    var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
-    //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
-    //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
-
-
-    /**
-    * The auth methods this class support
-    * @var array
-    */
-    var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
-
-
-    /**
-    * The capability response
-    * @var array
-    */
-    var $_capability;
-
-   /*
-    * Constructor. Sets up the object variables, and instantiates
-    * the socket object.
-    *
-    */
-
-
-    function Net_POP3()
-    {
-        $this->_timestamp =  ''; // Used for APOP
-        $this->_maildrop  =  array();
-        $this->_timeout   =  3;
-        $this->_state     =  NET_POP3_STATE_DISCONNECTED;
-        $this->_socket    =& new Net_Socket();
-        /*
-        * Include the Auth_SASL package.  If the package is not available,
-        * we disable the authentication methods that depend upon it.
-        */
-        if ((@include_once 'Auth/SASL.php') == false) {
-            if($this->_debug){
-                echo "AUTH_SASL NOT PRESENT!\n";
-            }
-            foreach($this->supportedSASLAuthMethods as $SASLMethod){
-                $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
-                if($this->_debug){
-                    echo "DISABLING METHOD $SASLMethod\n";
-                }
-                unset($this->supportedAuthMethods[$pos]);
-            }
-        }
-
-
-
-    }
-
-
-    /**
-    * Handles the errors the class can find
-    * on the server
-    *
-    * @access private
-    * @return PEAR_Error
-    */
-
-    function _raiseError($msg, $code =-1)
-    {
-    include_once 'PEAR.php';
-    return PEAR::raiseError($msg, $code);
-    }
-
-
-    
-    /*
-    * Connects to the given host on the given port.
-    * Also looks for the timestamp in the greeting
-    * needed for APOP authentication
-    *
-    * @param  string $host Hostname/IP address to connect to
-    * @param  string $port Port to use to connect to on host
-    * @return bool  Success/Failure
-    */
-    function connect($host = 'localhost', $port = 110)
-    {
-        $this->_host = $host;
-        $this->_port = $port;
-
-        $result = $this->_socket->connect($host, $port, false, $this->_timeout);
-        if ($result === true) {
-            $data = $this->_recvLn();
-
-            if( $this->_checkResponse($data) ){
-            // if the response begins with '+OK' ...
-//            if (@substr(strtoupper($data), 0, 3) == '+OK') {
-                // Check for string matching apop timestamp
-                if (preg_match('/<.+@.+>/U', $data, $matches)) {
-                    $this->_timestamp = $matches[0];
-                }
-                $this->_maildrop = array();
-                $this->_state    = NET_POP3_STATE_AUTHORISATION;
-
-                return true;
-            }
-        }
-
-        $this->_socket->disconnect();
-        return false;
-    }
-
-    /*
-    * Disconnect function. Sends the QUIT command
-    * and closes the socket.
-    *
-    * @return bool Success/Failure
-    */
-    function disconnect()
-    {
-        return $this->_cmdQuit();
-    }
-
-    /*
-    * Performs the login procedure. If there is a timestamp
-    * stored, APOP will be tried first, then basic USER/PASS.
-    *
-    * @param  string $user Username to use
-    * @param  string $pass Password to use
-    * @param  mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5");
-    *          Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' 
-    * @return mixed  true on Success/ PEAR_ERROR on error
-    */
-    function login($user, $pass, $apop = true)
-    {
-        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
-
-            if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){
-                return $ret;
-            }
-            if( ! PEAR::isError($ret)){
-                $this->_state = NET_POP3_STATE_TRANSACTION;
-                return true;
-            }
-
-        }
-        return $this->_raiseError('Generic login error' , 1);
-    }
-
-
-
-    /**
-    * Parses the response from the capability command. Stores
-    * the result in $this->_capability
-    *
-    * @access private
-    */
-    function _parseCapability()
-    {
-
-        if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){
-            $data = $this->_getMultiline();
-        }else {
-            // CAPA command not supported, reset data var
-            //  to avoid Notice errors of preg_split on an object
-            $data = '';
-        }
-        $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
-
-        for ($i = 0; $i < count($data); $i++) {
-
-            $capa='';
-            if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
-
-                $capa=strtolower($matches[1]);
-                switch ($capa) {
-                    case 'implementation':
-                        $this->_capability['implementation'] = $matches[3];
-                        break;
-                    case 'sasl':
-                        $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
-                        break;
-                    default :
-                        $this->_capability[$capa] = $matches[2];
-                        break;
-                }
-            }
-        }
-    }
-
-
-
-
-    /**
-     * Returns the name of the best authentication method that the server
-     * has advertised.
-     *
-     * @param string if !=null,authenticate with this method ($userMethod).
-     *
-     * @return mixed    Returns a string containing the name of the best
-     *                  supported authentication method or a PEAR_Error object
-     *                  if a failure condition is encountered.
-     * @access private
-     * @since  1.0
-     */
-    function _getBestAuthMethod($userMethod = null)
-    {
-
-/*
-       return 'USER';
-       return 'APOP';
-       return 'DIGEST-MD5';
-       return 'CRAM-MD5';
-*/
-
-
-        $this->_parseCapability();
-
-        //unset($this->_capability['sasl']);
-
-       if( isset($this->_capability['sasl']) ){
-           $serverMethods=$this->_capability['sasl'];
-       }else{
-            $serverMethods=array('USER');
-            // Check for timestamp before attempting APOP
-            if ($this->_timestamp != null)
-            {
-                $serverMethods[] = 'APOP';
-            }
-       }
-
-        if($userMethod !== null && $userMethod !== true ){
-            $methods = array();
-            $methods[] = $userMethod;
-            return $userMethod;
-        }else{
-            $methods = $this->supportedAuthMethods;
-        }
-
-        if( ($methods != null) && ($serverMethods != null)){
-
-            foreach ( $methods as $method ) {
-
-                if ( in_array( $method , $serverMethods ) ) {
-                    return $method;
-                }
-            }
-            $serverMethods=implode(',' , $serverMethods );
-            $myMethods=implode(',' ,$this->supportedAuthMethods);
-            return $this->_raiseError("$method NOT supported authentication method!. This server " .
-                "supports these methods: $serverMethods, but I support $myMethods");
-        }else{
-            return $this->_raiseError("This server don't support any Auth methods");
-        }
-    }
-
-
-
-
-
-
-    /* Handles the authentication using any known method
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
-     *
-     * @return mixed  string or PEAR_Error
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _cmdAuthenticate($uid , $pwd , $userMethod = null )
-    {
-
-
-        if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
-            return $method;
-        }
-
-        switch ($method) {
-            case 'DIGEST-MD5':
-                $result = $this->_authDigest_MD5( $uid , $pwd );
-                break;
-            case 'CRAM-MD5':
-                $result = $this->_authCRAM_MD5( $uid , $pwd );
-                break;
-            case 'LOGIN':
-                $result = $this->_authLOGIN( $uid , $pwd );
-                break;
-            case 'PLAIN':
-                $result = $this->_authPLAIN( $uid , $pwd );
-                break;
-            case 'APOP':
-                $result = $this->_cmdApop( $uid , $pwd );
-                // if APOP fails fallback to USER auth
-                if( PEAR::isError( $result ) ){
-                    //echo "APOP FAILED!!!\n";
-                    $result=$this->_authUSER( $uid , $pwd );
-                }
-                break;
-            case 'USER':
-                $result = $this->_authUSER( $uid , $pwd );
-            break;
-
-
-            default :
-                $result = $this->_raiseError( "$method is not a supported authentication method" );
-                break;
-        }
-        return $result;
-    }
-
-
-
-
-     /* Authenticates the user using the USER-PASS method.
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     *
-     * @return mixed    true on success or PEAR_Error on failure
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _authUSER($user, $pass  )
-    {
-        if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){
-            return $ret;
-        }
-        if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){
-            return $ret;
-        }
-        return true;
-    }
-
-
-
-
-
-
-
-
-     /* Authenticates the user using the PLAIN method.
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     *
-     * @return array Returns an array containing the response
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _authPLAIN($user, $pass  )
-    {
-        $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );
-
-        if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {
-            return $ret;
-        }
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ){
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-        
-        return true;
-    }
-
-
-
-     /* Authenticates the user using the PLAIN method.
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     *
-     * @return array Returns an array containing the response
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _authLOGIN($user, $pass  )
-    {
-        $this->_send('AUTH LOGIN');
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-
-
-        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {
-            return $ret;
-        }
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-
-        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {
-            return $ret;
-        }
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        return $this->_checkResponse($challenge);
-    }
-
-
-
-
-
-     /* Authenticates the user using the CRAM-MD5 method.
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     *
-     * @return array Returns an array containing the response
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _authCRAM_MD5($uid, $pwd )
-    {
-        if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {
-            return $ret;
-        }
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-
-        // remove '+ '
-        
-        $challenge=substr($challenge,2);
-        
-        $challenge = base64_decode( $challenge );
-
-        $cram = &Auth_SASL::factory('crammd5');
-        $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
-
-
-        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
-            return $error;
-        }
-        if ( PEAR::isError( $ret = $this->_recvLn() ) ) {
-            return $ret;
-        }
-        //echo "RET:$ret\n";
-        return $this->_checkResponse($ret);
-    }
-
-
-
-     /* Authenticates the user using the DIGEST-MD5 method.
-     *
-     * @param string The userid to authenticate as.
-     * @param string The password to authenticate with.
-     * @param string The efective user
-     *
-     * @return array Returns an array containing the response
-     *
-     * @access private
-     * @since  1.0
-     */
-    function _authDigest_MD5($uid, $pwd)
-    {
-        if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {
-            return $ret;
-        }
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-
-        // remove '+ '
-        $challenge=substr($challenge,2);
-
-        $challenge = base64_decode( $challenge );
-        $digest = &Auth_SASL::factory('digestmd5');
-        $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));
-
-        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
-            return $error;
-        }
-
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
-            return $ret;
-        }
-         /*
-         * We don't use the protocol's third step because POP3 doesn't allow
-         * subsequent authentication, so we just silently ignore it.
-         */
-
-        if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {
-            return $challenge ;
-        }
-        
-        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
-            return $challenge;
-        }
-        
-        return $this->_checkResponse($challenge);
-        
-
-    }
-
-
-
-
-
-
-
-
-
-
-    /*
-    * Sends the APOP command
-    *
-    * @param  $user Username to send
-    * @param  $pass Password to send
-    * @return bool Success/Failure
-    */
-    function _cmdApop($user, $pass)
-    {
-        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
-
-            if (!empty($this->_timestamp)) {
-                if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){
-                    return $data;
-                }
-                $this->_state = NET_POP3_STATE_TRANSACTION;
-                return true;
-            }
-        }
-        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
-    }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    /*
-    * Returns the raw headers of the specified message.
-    *
-    * @param  integer $msg_id Message number
-    * @return mixed   Either raw headers or false on error
-    */
-    function getRawHeaders($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            return $this->_cmdTop($msg_id, 0);
-        }
-
-        return false;
-    }
-
-    /*
-    * Returns the  headers of the specified message in an
-    * associative array. Array keys are the header names, array
-    * values are the header values. In the case of multiple headers
-    * having the same names, eg Received:, the array value will be
-    * an indexed array of all the header values.
-    *
-    * @param  integer $msg_id Message number
-    * @return mixed   Either array of headers or false on error
-    */
-    function getParsedHeaders($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-
-            $raw_headers = rtrim($this->getRawHeaders($msg_id));
-
-            $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
-            $raw_headers = explode("\r\n", $raw_headers);
-            foreach ($raw_headers as $value) {
-                $name  = substr($value, 0, $pos = strpos($value, ':'));
-                $value = ltrim(substr($value, $pos + 1));
-                if (isset($headers[$name]) AND is_array($headers[$name])) {
-                    $headers[$name][] = $value;
-                } elseif (isset($headers[$name])) {
-                    $headers[$name] = array($headers[$name], $value);
-                } else {
-                    $headers[$name] = $value;
-                }
-            }
-
-            return $headers;
-        }
-
-        return false;
-    }
-
-    /*
-    * Returns the body of the message with given message number.
-    *
-    * @param  integer $msg_id Message number
-    * @return mixed   Either message body or false on error
-    */
-    function getBody($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            $msg = $this->_cmdRetr($msg_id);
-            return substr($msg, strpos($msg, "\r\n\r\n")+4);
-        }
-
-        return false;
-    }
-
-    /*
-    * Returns the entire message with given message number.
-    *
-    * @param  integer $msg_id Message number
-    * @return mixed   Either entire message or false on error
-    */
-    function getMsg($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            return $this->_cmdRetr($msg_id);
-        }
-
-        return false;
-    }
-
-    /*
-    * Returns the size of the maildrop
-    *
-    * @return mixed Either size of maildrop or false on error
-    */
-    function getSize()
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if (isset($this->_maildrop['size'])) {
-                return $this->_maildrop['size'];
-            } else {
-                list(, $size) = $this->_cmdStat();
-                return $size;
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Returns number of messages in this maildrop
-    *
-    * @return mixed Either number of messages or false on error
-    */
-    function numMsg()
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if (isset($this->_maildrop['num_msg'])) {
-                return $this->_maildrop['num_msg'];
-            } else {
-                list($num_msg, ) = $this->_cmdStat();
-                return $num_msg;
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Marks a message for deletion. Only will be deleted if the
-    * disconnect() method is called.
-    *
-    * @param  integer $msg_id Message to delete
-    * @return bool Success/Failure
-    */
-    function deleteMsg($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            return $this->_cmdDele($msg_id);
-        }
-
-        return false;
-    }
-
-    /*
-    * Combination of LIST/UIDL commands, returns an array
-    * of data
-    *
-    * @param  integer $msg_id Optional message number
-    * @return mixed Array of data or false on error
-    */
-    function getListing($msg_id = null)
-    {
-    
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if (!isset($msg_id)){
-            
-                $list=array();
-                if ($list = $this->_cmdList()) {
-                    if ($uidl = $this->_cmdUidl()) {
-                        foreach ($uidl as $i => $value) {
-                            $list[$i]['uidl'] = $value['uidl'];
-                        }
-                    }
-                    return $list;
-                }else{
-                    return array();
-                }
-            } else {
-                if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {
-                    return array_merge($list, $uidl);
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Sends the USER command
-    *
-    * @param  string $user Username to send
-    * @return bool  Success/Failure
-    */
-    function _cmdUser($user)
-    {
-        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
-            return $this->_sendCmd('USER ' . $user);
-        }
-        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
-    }
-
-
-    /*
-    * Sends the PASS command
-    *
-    * @param  string $pass Password to send
-    * @return bool  Success/Failure
-    */
-    function _cmdPass($pass)
-    {
-        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
-            return $this->_sendCmd('PASS ' . $pass);
-        }
-        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
-    }
-
-
-    /*
-    * Sends the STAT command
-    *
-    * @return mixed Indexed array of number of messages and
-    *               maildrop size, or false on error.
-    */
-    function _cmdStat()
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if(!PEAR::isError($data = $this->_sendCmd('STAT'))){
-                sscanf($data, '+OK %d %d', $msg_num, $size);
-                $this->_maildrop['num_msg'] = $msg_num;
-                $this->_maildrop['size']    = $size;
-
-                return array($msg_num, $size);
-            }
-        }
-        return false;
-    }
-
-
-    /*
-    * Sends the LIST command
-    *
-    * @param  integer $msg_id Optional message number
-    * @return mixed   Indexed array of msg_id/msg size or
-    *                 false on error
-    */
-    function _cmdList($msg_id = null)
-    {
-        $return=array();
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if (!isset($msg_id)) {
-                if(!PEAR::isError($data = $this->_sendCmd('LIST') )){
-                    $data = $this->_getMultiline();
-                    $data = explode("\r\n", $data);                    
-                    foreach ($data as $line) {
-                        if($line !=''){
-                            sscanf($line, '%s %s', $msg_id, $size);
-                            $return[] = array('msg_id' => $msg_id, 'size' => $size);
-                        }
-                    }
-                    return $return;
-                }
-            } else {
-                if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){
-                    if($data!=''){
-                        sscanf($data, '+OK %d %d', $msg_id, $size);
-                        return array('msg_id' => $msg_id, 'size' => $size);
-                    }
-                    return array();
-                }
-            }
-        }
-        
-
-        return false;
-    }
-
-
-    /*
-    * Sends the RETR command
-    *
-    * @param  integer $msg_id The message number to retrieve
-    * @return mixed   The message or false on error
-    */
-    function _cmdRetr($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){
-                $data = $this->_getMultiline();
-                return $data;
-            }
-        }
-
-        return false;
-    }
-
-
-    /*
-    * Sends the DELE command
-    *
-    * @param  integer $msg_id Message number to mark as deleted
-    * @return bool Success/Failure
-    */
-    function _cmdDele($msg_id)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            return $this->_sendCmd('DELE ' . $msg_id);
-        }
-
-        return false;
-    }
-
-
-    /*
-    * Sends the NOOP command
-    *
-    * @return bool Success/Failure
-    */
-    function _cmdNoop()
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Sends the RSET command
-    *
-    * @return bool Success/Failure
-    */
-    function _cmdRset()
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-            if(!PEAR::isError($data = $this->_sendCmd('RSET'))){
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Sends the QUIT command
-    *
-    * @return bool Success/Failure
-    */
-    function _cmdQuit()
-    {
-        $data = $this->_sendCmd('QUIT');
-        $this->_state = NET_POP3_STATE_DISCONNECTED;
-        $this->_socket->disconnect();
-
-        return (bool)$data;
-    }
-
-
-    /*
-    * Sends the TOP command
-    *
-    * @param  integer  $msg_id    Message number
-    * @param  integer  $num_lines Number of lines to retrieve
-    * @return mixed Message data or false on error
-    */
-    function _cmdTop($msg_id, $num_lines)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-
-            if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){
-                return $this->_getMultiline();
-            }
-        }
-
-        return false;
-    }
-
-    /*
-    * Sends the UIDL command
-    *
-    * @param  integer $msg_id Message number
-    * @return mixed indexed array of msg_id/uidl or false on error
-    */
-    function _cmdUidl($msg_id = null)
-    {
-        if ($this->_state == NET_POP3_STATE_TRANSACTION) {
-
-            if (!isset($msg_id)) {
-                if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){
-                    $data = $this->_getMultiline();
-                    $data = explode("\r\n", $data);
-                    foreach ($data as $line) {
-                        sscanf($line, '%d %s', $msg_id, $uidl);
-                        $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);
-                    }
-
-                    return $return;
-                }
-            } else {
-
-                $data = $this->_sendCmd('UIDL ' . $msg_id);
-                sscanf($data, '+OK %d %s', $msg_id, $uidl);
-                return array('msg_id' => $msg_id, 'uidl' => $uidl);
-            }
-        }
-
-        return false;
-    }
-
-
-
-
-
-
-
-
-
-    /*
-    * Sends a command, checks the reponse, and
-    * if good returns the reponse, other wise
-    * returns false.
-    *
-    * @param  string $cmd  Command to send (\r\n will be appended)
-    * @return mixed First line of response if successful, otherwise false
-    */
-    function _sendCmd($cmd)
-    {
-        if (PEAR::isError($result = $this->_send($cmd) )){
-            return $result ;
-        }
-
-        if (PEAR::isError($data = $this->_recvLn() )){
-            return $data;
-        }
-        
-        if ( strtoupper(substr($data, 0, 3)) == '+OK') {
-            return $data;
-        }
-        
-        
-        return $this->_raiseError($data);
-    }
-
-    /*
-    * Reads a multiline reponse and returns the data
-    *
-    * @return string The reponse.
-    */
-    function _getMultiline()
-    {
-        $data = '';
-        while(!PEAR::isError($tmp = $this->_recvLn() ) ) {
-            if($tmp == '.'){
-                return substr($data, 0, -2);
-            }
-            if (substr($tmp, 0, 2) == '..') {
-                $tmp = substr($tmp, 1);
-            }
-            $data .= $tmp . "\r\n";
-        }
-        return substr($data, 0, -2);
-    }
-
-
-   /**
-    * Sets the bebug state
-    *
-    * @param  bool $debug 
-    * @access public
-    * @return void
-    */
-    function setDebug($debug=true)
-    {
-        $this->_debug=$debug;
-    }
-
-
-
-
-
-   /**
-     * Send the given string of data to the server.
-     *
-     * @param   string  $data       The string of data to send.
-     *
-     * @return  mixed   True on success or a PEAR_Error object on failure.
-     *
-     * @access  private
-     * @since   1.0
-     */
-    function _send($data)
-    {
-        if ($this->_debug) {
-            echo "C: $data\n";
-        }
-
-        if (PEAR::isError($error = $this->_socket->writeLine($data))) {
-            return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
-        }
-        return true;
-    }
-
-
-
-     /**
-     * Receive the given string of data from the server.
-     *
-     * @return  mixed   a line of response on success or a PEAR_Error object on failure.
-     *
-     * @access  private
-     * @since  1.0
-     */
-    function _recvLn()
-    {
-        if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {
-            return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );
-        }
-        if($this->_debug){
-            // S: means this data was sent by  the POP3 Server
-            echo "S:$lastline\n" ;
-        }
-        return $lastline;
-    }
-
-     /**
-     * Checks de server Response
-     *
-     * @param  string $response the response
-     * @return  mixed   true on success or a PEAR_Error object on failure.
-     *
-     * @access  private
-     * @since  1.3.3
-     */
-
-    function _checkResponse($response)
-    {
-        if (@substr(strtoupper($response), 0, 3) == '+OK') {
-            return true;
-        }else{
-            if (@substr(strtoupper($response), 0, 4) == '-ERR') {
-                return $this->_raiseError($response);
-            }else{
-                if (@substr(strtoupper($response), 0, 2) == '+ ') {
-                    return true;
-                }
-            }
-    
-        }
-        return $this->_raiseError("Unknown Response ($response)");
-    }
-    
-
-
-}
-
-?>
+<?php\r
+// +-----------------------------------------------------------------------+\r
+// | Copyright (c) 2002, Richard Heyes                                     |\r
+// | All rights reserved.                                                  |\r
+// |                                                                       |\r
+// | Redistribution and use in source and binary forms, with or without    |\r
+// | modification, are permitted provided that the following conditions    |\r
+// | are met:                                                              |\r
+// |                                                                       |\r
+// | o Redistributions of source code must retain the above copyright      |\r
+// |   notice, this list of conditions and the following disclaimer.       |\r
+// | o Redistributions in binary form must reproduce the above copyright   |\r
+// |   notice, this list of conditions and the following disclaimer in the |\r
+// |   documentation and/or other materials provided with the distribution.|\r
+// | o The names of the authors may not be used to endorse or promote      |\r
+// |   products derived from this software without specific prior written  |\r
+// |   permission.                                                         |\r
+// |                                                                       |\r
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |\r
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |\r
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |\r
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |\r
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |\r
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |\r
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |\r
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |\r
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |\r
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |\r
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |\r
+// |                                                                       |\r
+// +-----------------------------------------------------------------------+\r
+// | Author: Richard Heyes <richard@phpguru.org>                           |\r
+// | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar>               |\r
+// +-----------------------------------------------------------------------+\r
+//\r
+// $Id: POP3.php,v 1.7 2006/09/29 14:44:19 hsur Exp $\r
+\r
+require_once('Net/Socket.php');\r
+\r
+\r
+\r
+/**\r
+*  +----------------------------- IMPORTANT ------------------------------+\r
+*  | Usage of this class compared to native php extensions such as IMAP   |\r
+*  | is slow and may be feature deficient. If available you are STRONGLY  |\r
+*  | recommended to use the php extensions.                               |\r
+*  +----------------------------------------------------------------------+\r
+*\r
+* POP3 Access Class\r
+*\r
+* For usage see the example script\r
+*/\r
+\r
+define('NET_POP3_STATE_DISCONNECTED',  1, true);\r
+define('NET_POP3_STATE_AUTHORISATION', 2, true);\r
+define('NET_POP3_STATE_TRANSACTION',   4, true);\r
+\r
+class Net_POP3 {\r
+\r
+    /*\r
+    * Some basic information about the mail drop\r
+    * garnered from the STAT command\r
+    *\r
+    * @var array\r
+    */\r
+    var $_maildrop;\r
+\r
+    /*\r
+    * Used for APOP to store the timestamp\r
+    *\r
+    * @var string\r
+    */\r
+    var $_timestamp;\r
+\r
+    /*\r
+    * Timeout that is passed to the socket object\r
+    *\r
+    * @var integer\r
+    */\r
+    var $_timeout;\r
+\r
+    /*\r
+    * Socket object\r
+    *\r
+    * @var object\r
+    */\r
+    var $_socket;\r
+\r
+    /*\r
+    * Current state of the connection. Used with the\r
+    * constants defined above.\r
+    *\r
+    * @var integer\r
+    */\r
+    var $_state;\r
+\r
+    /*\r
+    * Hostname to connect to\r
+    *\r
+    * @var string\r
+    */\r
+    var $_host;\r
+\r
+    /*\r
+    * Port to connect to\r
+    *\r
+    * @var integer\r
+    */\r
+    var $_port;\r
+\r
+    /**\r
+    * To allow class debuging\r
+    * @var boolean\r
+    */\r
+    var $_debug = false;\r
+\r
+\r
+    /**\r
+    * The auth methods this class support\r
+    * @var array\r
+    */\r
+    //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');\r
+    //Disabling DIGEST-MD5 for now\r
+    var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');\r
+    //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');\r
+    //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');\r
+\r
+\r
+    /**\r
+    * The auth methods this class support\r
+    * @var array\r
+    */\r
+    var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');\r
+\r
+\r
+    /**\r
+    * The capability response\r
+    * @var array\r
+    */\r
+    var $_capability;\r
+\r
+   /*\r
+    * Constructor. Sets up the object variables, and instantiates\r
+    * the socket object.\r
+    *\r
+    */\r
+\r
+\r
+    function Net_POP3()\r
+    {\r
+        $this->_timestamp =  ''; // Used for APOP\r
+        $this->_maildrop  =  array();\r
+        $this->_timeout   =  3;\r
+        $this->_state     =  NET_POP3_STATE_DISCONNECTED;\r
+        $this->_socket    =& new Net_Socket();\r
+        /*\r
+        * Include the Auth_SASL package.  If the package is not available,\r
+        * we disable the authentication methods that depend upon it.\r
+        */\r
+        if ((@include_once 'Auth/SASL.php') == false) {\r
+            if($this->_debug){\r
+                echo "AUTH_SASL NOT PRESENT!\n";\r
+            }\r
+            foreach($this->supportedSASLAuthMethods as $SASLMethod){\r
+                $pos = array_search( $SASLMethod, $this->supportedAuthMethods );\r
+                if($this->_debug){\r
+                    echo "DISABLING METHOD $SASLMethod\n";\r
+                }\r
+                unset($this->supportedAuthMethods[$pos]);\r
+            }\r
+        }\r
+\r
+\r
+\r
+    }\r
+\r
+\r
+    /**\r
+    * Handles the errors the class can find\r
+    * on the server\r
+    *\r
+    * @access private\r
+    * @return PEAR_Error\r
+    */\r
+\r
+    function _raiseError($msg, $code =-1)\r
+    {\r
+    include_once 'PEAR.php';\r
+    return PEAR::raiseError($msg, $code);\r
+    }\r
+\r
+\r
+    \r
+    /*\r
+    * Connects to the given host on the given port.\r
+    * Also looks for the timestamp in the greeting\r
+    * needed for APOP authentication\r
+    *\r
+    * @param  string $host Hostname/IP address to connect to\r
+    * @param  string $port Port to use to connect to on host\r
+    * @return bool  Success/Failure\r
+    */\r
+    function connect($host = 'localhost', $port = 110)\r
+    {\r
+        $this->_host = $host;\r
+        $this->_port = $port;\r
+\r
+        $result = $this->_socket->connect($host, $port, false, $this->_timeout);\r
+        if ($result === true) {\r
+            $data = $this->_recvLn();\r
+\r
+            if( $this->_checkResponse($data) ){\r
+            // if the response begins with '+OK' ...\r
+//            if (@substr(strtoupper($data), 0, 3) == '+OK') {\r
+                // Check for string matching apop timestamp\r
+                if (preg_match('/<.+@.+>/U', $data, $matches)) {\r
+                    $this->_timestamp = $matches[0];\r
+                }\r
+                $this->_maildrop = array();\r
+                $this->_state    = NET_POP3_STATE_AUTHORISATION;\r
+\r
+                return true;\r
+            }\r
+        }\r
+\r
+        $this->_socket->disconnect();\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Disconnect function. Sends the QUIT command\r
+    * and closes the socket.\r
+    *\r
+    * @return bool Success/Failure\r
+    */\r
+    function disconnect()\r
+    {\r
+        return $this->_cmdQuit();\r
+    }\r
+\r
+    /*\r
+    * Performs the login procedure. If there is a timestamp\r
+    * stored, APOP will be tried first, then basic USER/PASS.\r
+    *\r
+    * @param  string $user Username to use\r
+    * @param  string $pass Password to use\r
+    * @param  mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5");\r
+    *          Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' \r
+    * @return mixed  true on Success/ PEAR_ERROR on error\r
+    */\r
+    function login($user, $pass, $apop = true)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {\r
+\r
+            if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){\r
+                return $ret;\r
+            }\r
+            if( ! PEAR::isError($ret)){\r
+                $this->_state = NET_POP3_STATE_TRANSACTION;\r
+                return true;\r
+            }\r
+\r
+        }\r
+        return $this->_raiseError('Generic login error' , 1);\r
+    }\r
+\r
+\r
+\r
+    /**\r
+    * Parses the response from the capability command. Stores\r
+    * the result in $this->_capability\r
+    *\r
+    * @access private\r
+    */\r
+    function _parseCapability()\r
+    {\r
+\r
+        if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){\r
+            $data = $this->_getMultiline();\r
+        }else {\r
+            // CAPA command not supported, reset data var\r
+            //  to avoid Notice errors of preg_split on an object\r
+            $data = '';\r
+        }\r
+        $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);\r
+\r
+        for ($i = 0; $i < count($data); $i++) {\r
+\r
+            $capa='';\r
+            if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {\r
+\r
+                $capa=strtolower($matches[1]);\r
+                switch ($capa) {\r
+                    case 'implementation':\r
+                        $this->_capability['implementation'] = $matches[3];\r
+                        break;\r
+                    case 'sasl':\r
+                        $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);\r
+                        break;\r
+                    default :\r
+                        $this->_capability[$capa] = $matches[2];\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+\r
+\r
+    /**\r
+     * Returns the name of the best authentication method that the server\r
+     * has advertised.\r
+     *\r
+     * @param string if !=null,authenticate with this method ($userMethod).\r
+     *\r
+     * @return mixed    Returns a string containing the name of the best\r
+     *                  supported authentication method or a PEAR_Error object\r
+     *                  if a failure condition is encountered.\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _getBestAuthMethod($userMethod = null)\r
+    {\r
+\r
+/*\r
+       return 'USER';\r
+       return 'APOP';\r
+       return 'DIGEST-MD5';\r
+       return 'CRAM-MD5';\r
+*/\r
+\r
+\r
+        $this->_parseCapability();\r
+\r
+        //unset($this->_capability['sasl']);\r
+\r
+       if( isset($this->_capability['sasl']) ){\r
+           $serverMethods=$this->_capability['sasl'];\r
+       }else{\r
+            $serverMethods=array('USER');\r
+            // Check for timestamp before attempting APOP\r
+            if ($this->_timestamp != null)\r
+            {\r
+                $serverMethods[] = 'APOP';\r
+            }\r
+       }\r
+\r
+        if($userMethod !== null && $userMethod !== true ){\r
+            $methods = array();\r
+            $methods[] = $userMethod;\r
+            return $userMethod;\r
+        }else{\r
+            $methods = $this->supportedAuthMethods;\r
+        }\r
+\r
+        if( ($methods != null) && ($serverMethods != null)){\r
+\r
+            foreach ( $methods as $method ) {\r
+\r
+                if ( in_array( $method , $serverMethods ) ) {\r
+                    return $method;\r
+                }\r
+            }\r
+            $serverMethods=implode(',' , $serverMethods );\r
+            $myMethods=implode(',' ,$this->supportedAuthMethods);\r
+            return $this->_raiseError("$method NOT supported authentication method!. This server " .\r
+                "supports these methods: $serverMethods, but I support $myMethods");\r
+        }else{\r
+            return $this->_raiseError("This server don't support any Auth methods");\r
+        }\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+\r
+    /* Handles the authentication using any known method\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )\r
+     *\r
+     * @return mixed  string or PEAR_Error\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _cmdAuthenticate($uid , $pwd , $userMethod = null )\r
+    {\r
+\r
+\r
+        if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {\r
+            return $method;\r
+        }\r
+\r
+        switch ($method) {\r
+            case 'DIGEST-MD5':\r
+                $result = $this->_authDigest_MD5( $uid , $pwd );\r
+                break;\r
+            case 'CRAM-MD5':\r
+                $result = $this->_authCRAM_MD5( $uid , $pwd );\r
+                break;\r
+            case 'LOGIN':\r
+                $result = $this->_authLOGIN( $uid , $pwd );\r
+                break;\r
+            case 'PLAIN':\r
+                $result = $this->_authPLAIN( $uid , $pwd );\r
+                break;\r
+            case 'APOP':\r
+                $result = $this->_cmdApop( $uid , $pwd );\r
+                // if APOP fails fallback to USER auth\r
+                if( PEAR::isError( $result ) ){\r
+                    //echo "APOP FAILED!!!\n";\r
+                    $result=$this->_authUSER( $uid , $pwd );\r
+                }\r
+                break;\r
+            case 'USER':\r
+                $result = $this->_authUSER( $uid , $pwd );\r
+            break;\r
+\r
+\r
+            default :\r
+                $result = $this->_raiseError( "$method is not a supported authentication method" );\r
+                break;\r
+        }\r
+        return $result;\r
+    }\r
+\r
+\r
+\r
+\r
+     /* Authenticates the user using the USER-PASS method.\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     *\r
+     * @return mixed    true on success or PEAR_Error on failure\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _authUSER($user, $pass  )\r
+    {\r
+        if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){\r
+            return $ret;\r
+        }\r
+        if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){\r
+            return $ret;\r
+        }\r
+        return true;\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+     /* Authenticates the user using the PLAIN method.\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     *\r
+     * @return array Returns an array containing the response\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _authPLAIN($user, $pass  )\r
+    {\r
+        $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );\r
+\r
+        if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {\r
+            return $ret;\r
+        }\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ){\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+        \r
+        return true;\r
+    }\r
+\r
+\r
+\r
+     /* Authenticates the user using the PLAIN method.\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     *\r
+     * @return array Returns an array containing the response\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _authLOGIN($user, $pass  )\r
+    {\r
+        $this->_send('AUTH LOGIN');\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+\r
+\r
+        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {\r
+            return $ret;\r
+        }\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+\r
+        if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {\r
+            return $ret;\r
+        }\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        return $this->_checkResponse($challenge);\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+     /* Authenticates the user using the CRAM-MD5 method.\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     *\r
+     * @return array Returns an array containing the response\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _authCRAM_MD5($uid, $pwd )\r
+    {\r
+        if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {\r
+            return $ret;\r
+        }\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+\r
+        // remove '+ '\r
+        \r
+        $challenge=substr($challenge,2);\r
+        \r
+        $challenge = base64_decode( $challenge );\r
+\r
+        $cram = &Auth_SASL::factory('crammd5');\r
+        $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );\r
+\r
+\r
+        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {\r
+            return $error;\r
+        }\r
+        if ( PEAR::isError( $ret = $this->_recvLn() ) ) {\r
+            return $ret;\r
+        }\r
+        //echo "RET:$ret\n";\r
+        return $this->_checkResponse($ret);\r
+    }\r
+\r
+\r
+\r
+     /* Authenticates the user using the DIGEST-MD5 method.\r
+     *\r
+     * @param string The userid to authenticate as.\r
+     * @param string The password to authenticate with.\r
+     * @param string The efective user\r
+     *\r
+     * @return array Returns an array containing the response\r
+     *\r
+     * @access private\r
+     * @since  1.0\r
+     */\r
+    function _authDigest_MD5($uid, $pwd)\r
+    {\r
+        if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {\r
+            return $ret;\r
+        }\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+\r
+        // remove '+ '\r
+        $challenge=substr($challenge,2);\r
+\r
+        $challenge = base64_decode( $challenge );\r
+        $digest = &Auth_SASL::factory('digestmd5');\r
+        $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));\r
+\r
+        if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {\r
+            return $error;\r
+        }\r
+\r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        if( PEAR::isError($ret=$this->_checkResponse($challenge) )){\r
+            return $ret;\r
+        }\r
+         /*\r
+         * We don't use the protocol's third step because POP3 doesn't allow\r
+         * subsequent authentication, so we just silently ignore it.\r
+         */\r
+\r
+        if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {\r
+            return $challenge ;\r
+        }\r
+        \r
+        if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {\r
+            return $challenge;\r
+        }\r
+        \r
+        return $this->_checkResponse($challenge);\r
+        \r
+\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+    /*\r
+    * Sends the APOP command\r
+    *\r
+    * @param  $user Username to send\r
+    * @param  $pass Password to send\r
+    * @return bool Success/Failure\r
+    */\r
+    function _cmdApop($user, $pass)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {\r
+\r
+            if (!empty($this->_timestamp)) {\r
+                if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){\r
+                    return $data;\r
+                }\r
+                $this->_state = NET_POP3_STATE_TRANSACTION;\r
+                return true;\r
+            }\r
+        }\r
+        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+    /*\r
+    * Returns the raw headers of the specified message.\r
+    *\r
+    * @param  integer $msg_id Message number\r
+    * @return mixed   Either raw headers or false on error\r
+    */\r
+    function getRawHeaders($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            return $this->_cmdTop($msg_id, 0);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Returns the  headers of the specified message in an\r
+    * associative array. Array keys are the header names, array\r
+    * values are the header values. In the case of multiple headers\r
+    * having the same names, eg Received:, the array value will be\r
+    * an indexed array of all the header values.\r
+    *\r
+    * @param  integer $msg_id Message number\r
+    * @return mixed   Either array of headers or false on error\r
+    */\r
+    function getParsedHeaders($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+\r
+            $raw_headers = rtrim($this->getRawHeaders($msg_id));\r
+\r
+            $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers\r
+            $raw_headers = explode("\r\n", $raw_headers);\r
+            foreach ($raw_headers as $value) {\r
+                $name  = substr($value, 0, $pos = strpos($value, ':'));\r
+                $value = ltrim(substr($value, $pos + 1));\r
+                if (isset($headers[$name]) AND is_array($headers[$name])) {\r
+                    $headers[$name][] = $value;\r
+                } elseif (isset($headers[$name])) {\r
+                    $headers[$name] = array($headers[$name], $value);\r
+                } else {\r
+                    $headers[$name] = $value;\r
+                }\r
+            }\r
+\r
+            return $headers;\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Returns the body of the message with given message number.\r
+    *\r
+    * @param  integer $msg_id Message number\r
+    * @return mixed   Either message body or false on error\r
+    */\r
+    function getBody($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            $msg = $this->_cmdRetr($msg_id);\r
+            return substr($msg, strpos($msg, "\r\n\r\n")+4);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Returns the entire message with given message number.\r
+    *\r
+    * @param  integer $msg_id Message number\r
+    * @return mixed   Either entire message or false on error\r
+    */\r
+    function getMsg($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            return $this->_cmdRetr($msg_id);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Returns the size of the maildrop\r
+    *\r
+    * @return mixed Either size of maildrop or false on error\r
+    */\r
+    function getSize()\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if (isset($this->_maildrop['size'])) {\r
+                return $this->_maildrop['size'];\r
+            } else {\r
+                list(, $size) = $this->_cmdStat();\r
+                return $size;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Returns number of messages in this maildrop\r
+    *\r
+    * @return mixed Either number of messages or false on error\r
+    */\r
+    function numMsg()\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if (isset($this->_maildrop['num_msg'])) {\r
+                return $this->_maildrop['num_msg'];\r
+            } else {\r
+                list($num_msg, ) = $this->_cmdStat();\r
+                return $num_msg;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Marks a message for deletion. Only will be deleted if the\r
+    * disconnect() method is called.\r
+    *\r
+    * @param  integer $msg_id Message to delete\r
+    * @return bool Success/Failure\r
+    */\r
+    function deleteMsg($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            return $this->_cmdDele($msg_id);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Combination of LIST/UIDL commands, returns an array\r
+    * of data\r
+    *\r
+    * @param  integer $msg_id Optional message number\r
+    * @return mixed Array of data or false on error\r
+    */\r
+    function getListing($msg_id = null)\r
+    {\r
+    \r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if (!isset($msg_id)){\r
+            \r
+                $list=array();\r
+                if ($list = $this->_cmdList()) {\r
+                    if ($uidl = $this->_cmdUidl()) {\r
+                        foreach ($uidl as $i => $value) {\r
+                            $list[$i]['uidl'] = $value['uidl'];\r
+                        }\r
+                    }\r
+                    return $list;\r
+                }else{\r
+                    return array();\r
+                }\r
+            } else {\r
+                if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {\r
+                    return array_merge($list, $uidl);\r
+                }\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Sends the USER command\r
+    *\r
+    * @param  string $user Username to send\r
+    * @return bool  Success/Failure\r
+    */\r
+    function _cmdUser($user)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {\r
+            return $this->_sendCmd('USER ' . $user);\r
+        }\r
+        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the PASS command\r
+    *\r
+    * @param  string $pass Password to send\r
+    * @return bool  Success/Failure\r
+    */\r
+    function _cmdPass($pass)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_AUTHORISATION) {\r
+            return $this->_sendCmd('PASS ' . $pass);\r
+        }\r
+        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the STAT command\r
+    *\r
+    * @return mixed Indexed array of number of messages and\r
+    *               maildrop size, or false on error.\r
+    */\r
+    function _cmdStat()\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if(!PEAR::isError($data = $this->_sendCmd('STAT'))){\r
+                sscanf($data, '+OK %d %d', $msg_num, $size);\r
+                $this->_maildrop['num_msg'] = $msg_num;\r
+                $this->_maildrop['size']    = $size;\r
+\r
+                return array($msg_num, $size);\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the LIST command\r
+    *\r
+    * @param  integer $msg_id Optional message number\r
+    * @return mixed   Indexed array of msg_id/msg size or\r
+    *                 false on error\r
+    */\r
+    function _cmdList($msg_id = null)\r
+    {\r
+        $return=array();\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if (!isset($msg_id)) {\r
+                if(!PEAR::isError($data = $this->_sendCmd('LIST') )){\r
+                    $data = $this->_getMultiline();\r
+                    $data = explode("\r\n", $data);                    \r
+                    foreach ($data as $line) {\r
+                        if($line !=''){\r
+                            sscanf($line, '%s %s', $msg_id, $size);\r
+                            $return[] = array('msg_id' => $msg_id, 'size' => $size);\r
+                        }\r
+                    }\r
+                    return $return;\r
+                }\r
+            } else {\r
+                if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){\r
+                    if($data!=''){\r
+                        sscanf($data, '+OK %d %d', $msg_id, $size);\r
+                        return array('msg_id' => $msg_id, 'size' => $size);\r
+                    }\r
+                    return array();\r
+                }\r
+            }\r
+        }\r
+        \r
+\r
+        return false;\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the RETR command\r
+    *\r
+    * @param  integer $msg_id The message number to retrieve\r
+    * @return mixed   The message or false on error\r
+    */\r
+    function _cmdRetr($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){\r
+                $data = $this->_getMultiline();\r
+                return $data;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the DELE command\r
+    *\r
+    * @param  integer $msg_id Message number to mark as deleted\r
+    * @return bool Success/Failure\r
+    */\r
+    function _cmdDele($msg_id)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            return $this->_sendCmd('DELE ' . $msg_id);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the NOOP command\r
+    *\r
+    * @return bool Success/Failure\r
+    */\r
+    function _cmdNoop()\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){\r
+                return true;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Sends the RSET command\r
+    *\r
+    * @return bool Success/Failure\r
+    */\r
+    function _cmdRset()\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+            if(!PEAR::isError($data = $this->_sendCmd('RSET'))){\r
+                return true;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Sends the QUIT command\r
+    *\r
+    * @return bool Success/Failure\r
+    */\r
+    function _cmdQuit()\r
+    {\r
+        $data = $this->_sendCmd('QUIT');\r
+        $this->_state = NET_POP3_STATE_DISCONNECTED;\r
+        $this->_socket->disconnect();\r
+\r
+        return (bool)$data;\r
+    }\r
+\r
+\r
+    /*\r
+    * Sends the TOP command\r
+    *\r
+    * @param  integer  $msg_id    Message number\r
+    * @param  integer  $num_lines Number of lines to retrieve\r
+    * @return mixed Message data or false on error\r
+    */\r
+    function _cmdTop($msg_id, $num_lines)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+\r
+            if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){\r
+                return $this->_getMultiline();\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /*\r
+    * Sends the UIDL command\r
+    *\r
+    * @param  integer $msg_id Message number\r
+    * @return mixed indexed array of msg_id/uidl or false on error\r
+    */\r
+    function _cmdUidl($msg_id = null)\r
+    {\r
+        if ($this->_state == NET_POP3_STATE_TRANSACTION) {\r
+\r
+            if (!isset($msg_id)) {\r
+                if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){\r
+                    $data = $this->_getMultiline();\r
+                    $data = explode("\r\n", $data);\r
+                    foreach ($data as $line) {\r
+                        sscanf($line, '%d %s', $msg_id, $uidl);\r
+                        $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);\r
+                    }\r
+\r
+                    return $return;\r
+                }\r
+            } else {\r
+\r
+                $data = $this->_sendCmd('UIDL ' . $msg_id);\r
+                sscanf($data, '+OK %d %s', $msg_id, $uidl);\r
+                return array('msg_id' => $msg_id, 'uidl' => $uidl);\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+    /*\r
+    * Sends a command, checks the reponse, and\r
+    * if good returns the reponse, other wise\r
+    * returns false.\r
+    *\r
+    * @param  string $cmd  Command to send (\r\n will be appended)\r
+    * @return mixed First line of response if successful, otherwise false\r
+    */\r
+    function _sendCmd($cmd)\r
+    {\r
+        if (PEAR::isError($result = $this->_send($cmd) )){\r
+            return $result ;\r
+        }\r
+\r
+        if (PEAR::isError($data = $this->_recvLn() )){\r
+            return $data;\r
+        }\r
+        \r
+        if ( strtoupper(substr($data, 0, 3)) == '+OK') {\r
+            return $data;\r
+        }\r
+        \r
+        \r
+        return $this->_raiseError($data);\r
+    }\r
+\r
+    /*\r
+    * Reads a multiline reponse and returns the data\r
+    *\r
+    * @return string The reponse.\r
+    */\r
+    function _getMultiline()\r
+    {\r
+        $data = '';\r
+        while(!PEAR::isError($tmp = $this->_recvLn() ) ) {\r
+            if($tmp == '.'){\r
+                return substr($data, 0, -2);\r
+            }\r
+            if (substr($tmp, 0, 2) == '..') {\r
+                $tmp = substr($tmp, 1);\r
+            }\r
+            $data .= $tmp . "\r\n";\r
+        }\r
+        return substr($data, 0, -2);\r
+    }\r
+\r
+\r
+   /**\r
+    * Sets the bebug state\r
+    *\r
+    * @param  bool $debug \r
+    * @access public\r
+    * @return void\r
+    */\r
+    function setDebug($debug=true)\r
+    {\r
+        $this->_debug=$debug;\r
+    }\r
+\r
+\r
+\r
+\r
+\r
+   /**\r
+     * Send the given string of data to the server.\r
+     *\r
+     * @param   string  $data       The string of data to send.\r
+     *\r
+     * @return  mixed   True on success or a PEAR_Error object on failure.\r
+     *\r
+     * @access  private\r
+     * @since   1.0\r
+     */\r
+    function _send($data)\r
+    {\r
+        if ($this->_debug) {\r
+            echo "C: $data\n";\r
+        }\r
+\r
+        if (PEAR::isError($error = $this->_socket->writeLine($data))) {\r
+            return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());\r
+        }\r
+        return true;\r
+    }\r
+\r
+\r
+\r
+     /**\r
+     * Receive the given string of data from the server.\r
+     *\r
+     * @return  mixed   a line of response on success or a PEAR_Error object on failure.\r
+     *\r
+     * @access  private\r
+     * @since  1.0\r
+     */\r
+    function _recvLn()\r
+    {\r
+        if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {\r
+            return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );\r
+        }\r
+        if($this->_debug){\r
+            // S: means this data was sent by  the POP3 Server\r
+            echo "S:$lastline\n" ;\r
+        }\r
+        return $lastline;\r
+    }\r
+\r
+     /**\r
+     * Checks de server Response\r
+     *\r
+     * @param  string $response the response\r
+     * @return  mixed   true on success or a PEAR_Error object on failure.\r
+     *\r
+     * @access  private\r
+     * @since  1.3.3\r
+     */\r
+\r
+    function _checkResponse($response)\r
+    {\r
+        if (@substr(strtoupper($response), 0, 3) == '+OK') {\r
+            return true;\r
+        }else{\r
+            if (@substr(strtoupper($response), 0, 4) == '-ERR') {\r
+                return $this->_raiseError($response);\r
+            }else{\r
+                if (@substr(strtoupper($response), 0, 2) == '+ ') {\r
+                    return true;\r
+                }\r
+            }\r
+    \r
+        }\r
+        return $this->_raiseError("Unknown Response ($response)");\r
+    }\r
+    \r
+\r
+\r
+}\r
+\r
+?>\r
index 72cd8a9..30c19b8 100644 (file)
-<?php
-//
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2003 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.0 of the PHP license,       |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Authors: Stig Bakken <ssb@php.net>                                   |
-// |          Chuck Hagenbuch <chuck@horde.org>                           |
-// +----------------------------------------------------------------------+
-//
-// $Id: Socket.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-
-require_once 'PEAR.php';
-
-define('NET_SOCKET_READ',  1);
-define('NET_SOCKET_WRITE', 2);
-define('NET_SOCKET_ERROR', 3);
-
-/**
- * Generalized Socket class.
- *
- * @version 1.1
- * @author Stig Bakken <ssb@php.net>
- * @author Chuck Hagenbuch <chuck@horde.org>
- */
-class Net_Socket extends PEAR {
-
-    /**
-     * Socket file pointer.
-     * @var resource $fp
-     */
-    var $fp = null;
-
-    /**
-     * Whether the socket is blocking. Defaults to true.
-     * @var boolean $blocking
-     */
-    var $blocking = true;
-
-    /**
-     * Whether the socket is persistent. Defaults to false.
-     * @var boolean $persistent
-     */
-    var $persistent = false;
-
-    /**
-     * The IP address to connect to.
-     * @var string $addr
-     */
-    var $addr = '';
-
-    /**
-     * The port number to connect to.
-     * @var integer $port
-     */
-    var $port = 0;
-
-    /**
-     * Number of seconds to wait on socket connections before assuming
-     * there's no more data. Defaults to no timeout.
-     * @var integer $timeout
-     */
-    var $timeout = false;
-
-    /**
-     * Number of bytes to read at a time in readLine() and
-     * readAll(). Defaults to 2048.
-     * @var integer $lineLength
-     */
-    var $lineLength = 2048;
-
-    /**
-     * Connect to the specified port. If called when the socket is
-     * already connected, it disconnects and connects again.
-     *
-     * @param string  $addr        IP address or host name.
-     * @param integer $port        TCP port number.
-     * @param boolean $persistent  (optional) Whether the connection is
-     *                             persistent (kept open between requests
-     *                             by the web server).
-     * @param integer $timeout     (optional) How long to wait for data.
-     * @param array   $options     See options for stream_context_create.
-     *
-     * @access public
-     *
-     * @return boolean | PEAR_Error  True on success or a PEAR_Error on failure.
-     */
-    function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
-    {
-        if (is_resource($this->fp)) {
-            @fclose($this->fp);
-            $this->fp = null;
-        }
-
-        if (!$addr) {
-            return $this->raiseError('$addr cannot be empty');
-        } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
-                  strstr($addr, '/') !== false) {
-            $this->addr = $addr;
-        } else {
-            $this->addr = @gethostbyname($addr);
-        }
-
-        $this->port = $port % 65536;
-
-        if ($persistent !== null) {
-            $this->persistent = $persistent;
-        }
-
-        if ($timeout !== null) {
-            $this->timeout = $timeout;
-        }
-
-        $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
-        $errno = 0;
-        $errstr = '';
-        if ($options && function_exists('stream_context_create')) {
-            if ($this->timeout) {
-                $timeout = $this->timeout;
-            } else {
-                $timeout = 0;
-            }
-            $context = stream_context_create($options);
-            $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
-        } else {
-            if ($this->timeout) {
-                $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
-            } else {
-                $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
-            }
-        }
-
-        if (!$fp) {
-            return $this->raiseError($errstr, $errno);
-        }
-
-        $this->fp = $fp;
-
-        return $this->setBlocking($this->blocking);
-    }
-
-    /**
-     * Disconnects from the peer, closes the socket.
-     *
-     * @access public
-     * @return mixed true on success or an error object otherwise
-     */
-    function disconnect()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        @fclose($this->fp);
-        $this->fp = null;
-        return true;
-    }
-
-    /**
-     * Find out if the socket is in blocking mode.
-     *
-     * @access public
-     * @return boolean  The current blocking mode.
-     */
-    function isBlocking()
-    {
-        return $this->blocking;
-    }
-
-    /**
-     * Sets whether the socket connection should be blocking or
-     * not. A read call to a non-blocking socket will return immediately
-     * if there is no data available, whereas it will block until there
-     * is data for blocking sockets.
-     *
-     * @param boolean $mode  True for blocking sockets, false for nonblocking.
-     * @access public
-     * @return mixed true on success or an error object otherwise
-     */
-    function setBlocking($mode)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $this->blocking = $mode;
-        socket_set_blocking($this->fp, $this->blocking);
-        return true;
-    }
-
-    /**
-     * Sets the timeout value on socket descriptor,
-     * expressed in the sum of seconds and microseconds
-     *
-     * @param integer $seconds  Seconds.
-     * @param integer $microseconds  Microseconds.
-     * @access public
-     * @return mixed true on success or an error object otherwise
-     */
-    function setTimeout($seconds, $microseconds)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return socket_set_timeout($this->fp, $seconds, $microseconds);
-    }
-
-    /**
-     * Returns information about an existing socket resource.
-     * Currently returns four entries in the result array:
-     *
-     * <p>
-     * timed_out (bool) - The socket timed out waiting for data<br>
-     * blocked (bool) - The socket was blocked<br>
-     * eof (bool) - Indicates EOF event<br>
-     * unread_bytes (int) - Number of bytes left in the socket buffer<br>
-     * </p>
-     *
-     * @access public
-     * @return mixed Array containing information about existing socket resource or an error object otherwise
-     */
-    function getStatus()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return socket_get_status($this->fp);
-    }
-
-    /**
-     * Get a specified line of data
-     *
-     * @access public
-     * @return $size bytes of data from the socket, or a PEAR_Error if
-     *         not connected.
-     */
-    function gets($size)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return @fgets($this->fp, $size);
-    }
-
-    /**
-     * Read a specified amount of data. This is guaranteed to return,
-     * and has the added benefit of getting everything in one fread()
-     * chunk; if you know the size of the data you're getting
-     * beforehand, this is definitely the way to go.
-     *
-     * @param integer $size  The number of bytes to read from the socket.
-     * @access public
-     * @return $size bytes of data from the socket, or a PEAR_Error if
-     *         not connected.
-     */
-    function read($size)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return @fread($this->fp, $size);
-    }
-
-    /**
-     * Write a specified amount of data.
-     *
-     * @param string  $data       Data to write.
-     * @param integer $blocksize  Amount of data to write at once.
-     *                            NULL means all at once.
-     *
-     * @access public
-     * @return mixed true on success or an error object otherwise
-     */
-    function write($data, $blocksize = null)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        if (is_null($blocksize) && !OS_WINDOWS) {
-            return fwrite($this->fp, $data);
-        } else {
-            if (is_null($blocksize)) {
-                $blocksize = 1024;
-            }
-
-            $pos = 0;
-            $size = strlen($data);
-            while ($pos < $size) {
-                $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
-                if ($written === false) {
-                    return false;
-                }
-                $pos += $written;
-            }
-
-            return $pos;
-        }
-    }
-
-    /**
-     * Write a line of data to the socket, followed by a trailing "\r\n".
-     *
-     * @access public
-     * @return mixed fputs result, or an error
-     */
-    function writeLine($data)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return fwrite($this->fp, $data . "\r\n");
-    }
-
-    /**
-     * Tests for end-of-file on a socket descriptor.
-     *
-     * @access public
-     * @return bool
-     */
-    function eof()
-    {
-        return (is_resource($this->fp) && feof($this->fp));
-    }
-
-    /**
-     * Reads a byte of data
-     *
-     * @access public
-     * @return 1 byte of data from the socket, or a PEAR_Error if
-     *         not connected.
-     */
-    function readByte()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        return ord(@fread($this->fp, 1));
-    }
-
-    /**
-     * Reads a word of data
-     *
-     * @access public
-     * @return 1 word of data from the socket, or a PEAR_Error if
-     *         not connected.
-     */
-    function readWord()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $buf = @fread($this->fp, 2);
-        return (ord($buf[0]) + (ord($buf[1]) << 8));
-    }
-
-    /**
-     * Reads an int of data
-     *
-     * @access public
-     * @return integer  1 int of data from the socket, or a PEAR_Error if
-     *                  not connected.
-     */
-    function readInt()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $buf = @fread($this->fp, 4);
-        return (ord($buf[0]) + (ord($buf[1]) << 8) +
-                (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
-    }
-
-    /**
-     * Reads a zero-terminated string of data
-     *
-     * @access public
-     * @return string, or a PEAR_Error if
-     *         not connected.
-     */
-    function readString()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $string = '';
-        while (($char = @fread($this->fp, 1)) != "\x00")  {
-            $string .= $char;
-        }
-        return $string;
-    }
-
-    /**
-     * Reads an IP Address and returns it in a dot formated string
-     *
-     * @access public
-     * @return Dot formated string, or a PEAR_Error if
-     *         not connected.
-     */
-    function readIPAddress()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $buf = @fread($this->fp, 4);
-        return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
-                       ord($buf[2]), ord($buf[3]));
-    }
-
-    /**
-     * Read until either the end of the socket or a newline, whichever
-     * comes first. Strips the trailing newline from the returned data.
-     *
-     * @access public
-     * @return All available data up to a newline, without that
-     *         newline, or until the end of the socket, or a PEAR_Error if
-     *         not connected.
-     */
-    function readLine()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $line = '';
-        $timeout = time() + $this->timeout;
-        while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
-            $line .= @fgets($this->fp, $this->lineLength);
-            if (substr($line, -1) == "\n") {
-                return rtrim($line, "\r\n");
-            }
-        }
-        return $line;
-    }
-
-    /**
-     * Read until the socket closes, or until there is no more data in
-     * the inner PHP buffer. If the inner buffer is empty, in blocking
-     * mode we wait for at least 1 byte of data. Therefore, in
-     * blocking mode, if there is no data at all to be read, this
-     * function will never exit (unless the socket is closed on the
-     * remote end).
-     *
-     * @access public
-     *
-     * @return string  All data until the socket closes, or a PEAR_Error if
-     *                 not connected.
-     */
-    function readAll()
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $data = '';
-        while (!feof($this->fp)) {
-            $data .= @fread($this->fp, $this->lineLength);
-        }
-        return $data;
-    }
-
-    /**
-     * Runs the equivalent of the select() system call on the socket
-     * with a timeout specified by tv_sec and tv_usec.
-     *
-     * @param integer $state    Which of read/write/error to check for.
-     * @param integer $tv_sec   Number of seconds for timeout.
-     * @param integer $tv_usec  Number of microseconds for timeout.
-     *
-     * @access public
-     * @return False if select fails, integer describing which of read/write/error
-     *         are ready, or PEAR_Error if not connected.
-     */
-    function select($state, $tv_sec, $tv_usec = 0)
-    {
-        if (!is_resource($this->fp)) {
-            return $this->raiseError('not connected');
-        }
-
-        $read = null;
-        $write = null;
-        $except = null;
-        if ($state & NET_SOCKET_READ) {
-            $read[] = $this->fp;
-        }
-        if ($state & NET_SOCKET_WRITE) {
-            $write[] = $this->fp;
-        }
-        if ($state & NET_SOCKET_ERROR) {
-            $except[] = $this->fp;
-        }
-        if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
-            return false;
-        }
-
-        $result = 0;
-        if (count($read)) {
-            $result |= NET_SOCKET_READ;
-        }
-        if (count($write)) {
-            $result |= NET_SOCKET_WRITE;
-        }
-        if (count($except)) {
-            $result |= NET_SOCKET_ERROR;
-        }
-        return $result;
-    }
-
-}
+<?php\r
+//\r
+// +----------------------------------------------------------------------+\r
+// | PHP Version 4                                                        |\r
+// +----------------------------------------------------------------------+\r
+// | Copyright (c) 1997-2003 The PHP Group                                |\r
+// +----------------------------------------------------------------------+\r
+// | This source file is subject to version 2.0 of the PHP license,       |\r
+// | that is bundled with this package in the file LICENSE, and is        |\r
+// | available at through the world-wide-web at                           |\r
+// | http://www.php.net/license/2_02.txt.                                 |\r
+// | If you did not receive a copy of the PHP license and are unable to   |\r
+// | obtain it through the world-wide-web, please send a note to          |\r
+// | license@php.net so we can mail you a copy immediately.               |\r
+// +----------------------------------------------------------------------+\r
+// | Authors: Stig Bakken <ssb@php.net>                                   |\r
+// |          Chuck Hagenbuch <chuck@horde.org>                           |\r
+// +----------------------------------------------------------------------+\r
+//\r
+// $Id: Socket.php,v 1.8 2006/11/27 17:14:39 hsur Exp $\r
+\r
+require_once 'PEAR.php';\r
+\r
+define('NET_SOCKET_READ',  1);\r
+define('NET_SOCKET_WRITE', 2);\r
+define('NET_SOCKET_ERROR', 3);\r
+\r
+/**\r
+ * Generalized Socket class.\r
+ *\r
+ * @version 1.1\r
+ * @author Stig Bakken <ssb@php.net>\r
+ * @author Chuck Hagenbuch <chuck@horde.org>\r
+ */\r
+class Net_Socket extends PEAR {\r
+\r
+    /**\r
+     * Socket file pointer.\r
+     * @var resource $fp\r
+     */\r
+    var $fp = null;\r
+\r
+    /**\r
+     * Whether the socket is blocking. Defaults to true.\r
+     * @var boolean $blocking\r
+     */\r
+    var $blocking = true;\r
+\r
+    /**\r
+     * Whether the socket is persistent. Defaults to false.\r
+     * @var boolean $persistent\r
+     */\r
+    var $persistent = false;\r
+\r
+    /**\r
+     * The IP address to connect to.\r
+     * @var string $addr\r
+     */\r
+    var $addr = '';\r
+\r
+    /**\r
+     * The port number to connect to.\r
+     * @var integer $port\r
+     */\r
+    var $port = 0;\r
+\r
+    /**\r
+     * Number of seconds to wait on socket connections before assuming\r
+     * there's no more data. Defaults to no timeout.\r
+     * @var integer $timeout\r
+     */\r
+    var $timeout = false;\r
+\r
+    /**\r
+     * Number of bytes to read at a time in readLine() and\r
+     * readAll(). Defaults to 2048.\r
+     * @var integer $lineLength\r
+     */\r
+    var $lineLength = 2048;\r
+\r
+    /**\r
+     * Connect to the specified port. If called when the socket is\r
+     * already connected, it disconnects and connects again.\r
+     *\r
+     * @param string  $addr        IP address or host name.\r
+     * @param integer $port        TCP port number.\r
+     * @param boolean $persistent  (optional) Whether the connection is\r
+     *                             persistent (kept open between requests\r
+     *                             by the web server).\r
+     * @param integer $timeout     (optional) How long to wait for data.\r
+     * @param array   $options     See options for stream_context_create.\r
+     *\r
+     * @access public\r
+     *\r
+     * @return boolean | PEAR_Error  True on success or a PEAR_Error on failure.\r
+     */\r
+    function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)\r
+    {\r
+        if (is_resource($this->fp)) {\r
+            @fclose($this->fp);\r
+            $this->fp = null;\r
+        }\r
+\r
+        if (!$addr) {\r
+            return $this->raiseError('$addr cannot be empty');\r
+        } elseif (strspn($addr, '.0123456789') == strlen($addr) ||\r
+                  strstr($addr, '/') !== false) {\r
+            $this->addr = $addr;\r
+        } else {\r
+            $this->addr = @gethostbyname($addr);\r
+        }\r
+\r
+        $this->port = $port % 65536;\r
+\r
+        if ($persistent !== null) {\r
+            $this->persistent = $persistent;\r
+        }\r
+\r
+        if ($timeout !== null) {\r
+            $this->timeout = $timeout;\r
+        }\r
+\r
+        $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';\r
+        $errno = 0;\r
+        $errstr = '';\r
+        if ($options && function_exists('stream_context_create')) {\r
+            if ($this->timeout) {\r
+                $timeout = $this->timeout;\r
+            } else {\r
+                $timeout = 0;\r
+            }\r
+            $context = stream_context_create($options);\r
+            $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);\r
+        } else {\r
+            if ($this->timeout) {\r
+                $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);\r
+            } else {\r
+                $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);\r
+            }\r
+        }\r
+\r
+        if (!$fp) {\r
+            return $this->raiseError($errstr, $errno);\r
+        }\r
+\r
+        $this->fp = $fp;\r
+\r
+        return $this->setBlocking($this->blocking);\r
+    }\r
+\r
+    /**\r
+     * Disconnects from the peer, closes the socket.\r
+     *\r
+     * @access public\r
+     * @return mixed true on success or an error object otherwise\r
+     */\r
+    function disconnect()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        @fclose($this->fp);\r
+        $this->fp = null;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Find out if the socket is in blocking mode.\r
+     *\r
+     * @access public\r
+     * @return boolean  The current blocking mode.\r
+     */\r
+    function isBlocking()\r
+    {\r
+        return $this->blocking;\r
+    }\r
+\r
+    /**\r
+     * Sets whether the socket connection should be blocking or\r
+     * not. A read call to a non-blocking socket will return immediately\r
+     * if there is no data available, whereas it will block until there\r
+     * is data for blocking sockets.\r
+     *\r
+     * @param boolean $mode  True for blocking sockets, false for nonblocking.\r
+     * @access public\r
+     * @return mixed true on success or an error object otherwise\r
+     */\r
+    function setBlocking($mode)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $this->blocking = $mode;\r
+        socket_set_blocking($this->fp, $this->blocking);\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Sets the timeout value on socket descriptor,\r
+     * expressed in the sum of seconds and microseconds\r
+     *\r
+     * @param integer $seconds  Seconds.\r
+     * @param integer $microseconds  Microseconds.\r
+     * @access public\r
+     * @return mixed true on success or an error object otherwise\r
+     */\r
+    function setTimeout($seconds, $microseconds)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return socket_set_timeout($this->fp, $seconds, $microseconds);\r
+    }\r
+\r
+    /**\r
+     * Returns information about an existing socket resource.\r
+     * Currently returns four entries in the result array:\r
+     *\r
+     * <p>\r
+     * timed_out (bool) - The socket timed out waiting for data<br>\r
+     * blocked (bool) - The socket was blocked<br>\r
+     * eof (bool) - Indicates EOF event<br>\r
+     * unread_bytes (int) - Number of bytes left in the socket buffer<br>\r
+     * </p>\r
+     *\r
+     * @access public\r
+     * @return mixed Array containing information about existing socket resource or an error object otherwise\r
+     */\r
+    function getStatus()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return socket_get_status($this->fp);\r
+    }\r
+\r
+    /**\r
+     * Get a specified line of data\r
+     *\r
+     * @access public\r
+     * @return $size bytes of data from the socket, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function gets($size)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return @fgets($this->fp, $size);\r
+    }\r
+\r
+    /**\r
+     * Read a specified amount of data. This is guaranteed to return,\r
+     * and has the added benefit of getting everything in one fread()\r
+     * chunk; if you know the size of the data you're getting\r
+     * beforehand, this is definitely the way to go.\r
+     *\r
+     * @param integer $size  The number of bytes to read from the socket.\r
+     * @access public\r
+     * @return $size bytes of data from the socket, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function read($size)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return @fread($this->fp, $size);\r
+    }\r
+\r
+    /**\r
+     * Write a specified amount of data.\r
+     *\r
+     * @param string  $data       Data to write.\r
+     * @param integer $blocksize  Amount of data to write at once.\r
+     *                            NULL means all at once.\r
+     *\r
+     * @access public\r
+     * @return mixed true on success or an error object otherwise\r
+     */\r
+    function write($data, $blocksize = null)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        if (is_null($blocksize) && !OS_WINDOWS) {\r
+            return fwrite($this->fp, $data);\r
+        } else {\r
+            if (is_null($blocksize)) {\r
+                $blocksize = 1024;\r
+            }\r
+\r
+            $pos = 0;\r
+            $size = strlen($data);\r
+            while ($pos < $size) {\r
+                $written = @fwrite($this->fp, substr($data, $pos, $blocksize));\r
+                if ($written === false) {\r
+                    return false;\r
+                }\r
+                $pos += $written;\r
+            }\r
+\r
+            return $pos;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Write a line of data to the socket, followed by a trailing "\r\n".\r
+     *\r
+     * @access public\r
+     * @return mixed fputs result, or an error\r
+     */\r
+    function writeLine($data)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return fwrite($this->fp, $data . "\r\n");\r
+    }\r
+\r
+    /**\r
+     * Tests for end-of-file on a socket descriptor.\r
+     *\r
+     * @access public\r
+     * @return bool\r
+     */\r
+    function eof()\r
+    {\r
+        return (is_resource($this->fp) && feof($this->fp));\r
+    }\r
+\r
+    /**\r
+     * Reads a byte of data\r
+     *\r
+     * @access public\r
+     * @return 1 byte of data from the socket, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function readByte()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        return ord(@fread($this->fp, 1));\r
+    }\r
+\r
+    /**\r
+     * Reads a word of data\r
+     *\r
+     * @access public\r
+     * @return 1 word of data from the socket, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function readWord()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $buf = @fread($this->fp, 2);\r
+        return (ord($buf[0]) + (ord($buf[1]) << 8));\r
+    }\r
+\r
+    /**\r
+     * Reads an int of data\r
+     *\r
+     * @access public\r
+     * @return integer  1 int of data from the socket, or a PEAR_Error if\r
+     *                  not connected.\r
+     */\r
+    function readInt()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $buf = @fread($this->fp, 4);\r
+        return (ord($buf[0]) + (ord($buf[1]) << 8) +\r
+                (ord($buf[2]) << 16) + (ord($buf[3]) << 24));\r
+    }\r
+\r
+    /**\r
+     * Reads a zero-terminated string of data\r
+     *\r
+     * @access public\r
+     * @return string, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function readString()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $string = '';\r
+        while (($char = @fread($this->fp, 1)) != "\x00")  {\r
+            $string .= $char;\r
+        }\r
+        return $string;\r
+    }\r
+\r
+    /**\r
+     * Reads an IP Address and returns it in a dot formated string\r
+     *\r
+     * @access public\r
+     * @return Dot formated string, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function readIPAddress()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $buf = @fread($this->fp, 4);\r
+        return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),\r
+                       ord($buf[2]), ord($buf[3]));\r
+    }\r
+\r
+    /**\r
+     * Read until either the end of the socket or a newline, whichever\r
+     * comes first. Strips the trailing newline from the returned data.\r
+     *\r
+     * @access public\r
+     * @return All available data up to a newline, without that\r
+     *         newline, or until the end of the socket, or a PEAR_Error if\r
+     *         not connected.\r
+     */\r
+    function readLine()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $line = '';\r
+        $timeout = time() + $this->timeout;\r
+        while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {\r
+            $line .= @fgets($this->fp, $this->lineLength);\r
+            if (substr($line, -1) == "\n") {\r
+                return rtrim($line, "\r\n");\r
+            }\r
+        }\r
+        return $line;\r
+    }\r
+\r
+    /**\r
+     * Read until the socket closes, or until there is no more data in\r
+     * the inner PHP buffer. If the inner buffer is empty, in blocking\r
+     * mode we wait for at least 1 byte of data. Therefore, in\r
+     * blocking mode, if there is no data at all to be read, this\r
+     * function will never exit (unless the socket is closed on the\r
+     * remote end).\r
+     *\r
+     * @access public\r
+     *\r
+     * @return string  All data until the socket closes, or a PEAR_Error if\r
+     *                 not connected.\r
+     */\r
+    function readAll()\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $data = '';\r
+        while (!feof($this->fp)) {\r
+            $data .= @fread($this->fp, $this->lineLength);\r
+        }\r
+        return $data;\r
+    }\r
+\r
+    /**\r
+     * Runs the equivalent of the select() system call on the socket\r
+     * with a timeout specified by tv_sec and tv_usec.\r
+     *\r
+     * @param integer $state    Which of read/write/error to check for.\r
+     * @param integer $tv_sec   Number of seconds for timeout.\r
+     * @param integer $tv_usec  Number of microseconds for timeout.\r
+     *\r
+     * @access public\r
+     * @return False if select fails, integer describing which of read/write/error\r
+     *         are ready, or PEAR_Error if not connected.\r
+     */\r
+    function select($state, $tv_sec, $tv_usec = 0)\r
+    {\r
+        if (!is_resource($this->fp)) {\r
+            return $this->raiseError('not connected');\r
+        }\r
+\r
+        $read = null;\r
+        $write = null;\r
+        $except = null;\r
+        if ($state & NET_SOCKET_READ) {\r
+            $read[] = $this->fp;\r
+        }\r
+        if ($state & NET_SOCKET_WRITE) {\r
+            $write[] = $this->fp;\r
+        }\r
+        if ($state & NET_SOCKET_ERROR) {\r
+            $except[] = $this->fp;\r
+        }\r
+        if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {\r
+            return false;\r
+        }\r
+\r
+        $result = 0;\r
+        if (count($read)) {\r
+            $result |= NET_SOCKET_READ;\r
+        }\r
+        if (count($write)) {\r
+            $result |= NET_SOCKET_WRITE;\r
+        }\r
+        if (count($except)) {\r
+            $result |= NET_SOCKET_ERROR;\r
+        }\r
+        return $result;\r
+    }\r
+\r
+}\r
index 1040bb3..5696f0d 100644 (file)
-<?php
-// +-----------------------------------------------------------------------+
-// | Copyright (c) 2002-2004, Richard Heyes                                |
-// | All rights reserved.                                                  |
-// |                                                                       |
-// | Redistribution and use in source and binary forms, with or without    |
-// | modification, are permitted provided that the following conditions    |
-// | are met:                                                              |
-// |                                                                       |
-// | o Redistributions of source code must retain the above copyright      |
-// |   notice, this list of conditions and the following disclaimer.       |
-// | o Redistributions in binary form must reproduce the above copyright   |
-// |   notice, this list of conditions and the following disclaimer in the |
-// |   documentation and/or other materials provided with the distribution.|
-// | o The names of the authors may not be used to endorse or promote      |
-// |   products derived from this software without specific prior written  |
-// |   permission.                                                         |
-// |                                                                       |
-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
-// |                                                                       |
-// +-----------------------------------------------------------------------+
-// | Author: Richard Heyes <richard at php net>                            |
-// +-----------------------------------------------------------------------+
-//
-// $Id: URL.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-//
-// Net_URL Class
-
-class Net_URL
-{
-    /**
-    * Full url
-    * @var string
-    */
-    var $url;
-
-    /**
-    * Protocol
-    * @var string
-    */
-    var $protocol;
-
-    /**
-    * Username
-    * @var string
-    */
-    var $username;
-
-    /**
-    * Password
-    * @var string
-    */
-    var $password;
-
-    /**
-    * Host
-    * @var string
-    */
-    var $host;
-
-    /**
-    * Port
-    * @var integer
-    */
-    var $port;
-
-    /**
-    * Path
-    * @var string
-    */
-    var $path;
-
-    /**
-    * Query string
-    * @var array
-    */
-    var $querystring;
-
-    /**
-    * Anchor
-    * @var string
-    */
-    var $anchor;
-
-    /**
-    * Whether to use []
-    * @var bool
-    */
-    var $useBrackets;
-
-    /**
-    * PHP4 Constructor
-    *
-    * @see __construct()
-    */
-    function Net_URL($url = null, $useBrackets = true)
-    {
-        $this->__construct($url, $useBrackets);
-    }
-
-    /**
-    * PHP5 Constructor
-    *
-    * Parses the given url and stores the various parts
-    * Defaults are used in certain cases
-    *
-    * @param string $url         Optional URL
-    * @param bool   $useBrackets Whether to use square brackets when
-    *                            multiple querystrings with the same name
-    *                            exist
-    */
-    function __construct($url = null, $useBrackets = true)
-    {
-        $HTTP_SERVER_VARS  = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
-
-        $this->useBrackets = $useBrackets;
-        $this->url         = $url;
-        $this->user        = '';
-        $this->pass        = '';
-        $this->host        = '';
-        $this->port        = 80;
-        $this->path        = '';
-        $this->querystring = array();
-        $this->anchor      = '';
-
-        // Only use defaults if not an absolute URL given
-        if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
-
-            $this->protocol    = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');
-
-            /**
-            * Figure out host/port
-            */
-            if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
-                $host = $matches[1];
-                if (!empty($matches[3])) {
-                    $port = $matches[3];
-                } else {
-                    $port = $this->getStandardPort($this->protocol);
-                }
-            }
-
-            $this->user        = '';
-            $this->pass        = '';
-            $this->host        = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
-            $this->port        = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
-            $this->path        = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
-            $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
-            $this->anchor      = '';
-        }
-
-        // Parse the url and store the various parts
-        if (!empty($url)) {
-            $urlinfo = parse_url($url);
-
-            // Default querystring
-            $this->querystring = array();
-
-            foreach ($urlinfo as $key => $value) {
-                switch ($key) {
-                    case 'scheme':
-                        $this->protocol = $value;
-                        $this->port     = $this->getStandardPort($value);
-                        break;
-
-                    case 'user':
-                    case 'pass':
-                    case 'host':
-                    case 'port':
-                        $this->$key = $value;
-                        break;
-
-                    case 'path':
-                        if ($value{0} == '/') {
-                            $this->path = $value;
-                        } else {
-                            $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
-                            $this->path = sprintf('%s/%s', $path, $value);
-                        }
-                        break;
-
-                    case 'query':
-                        $this->querystring = $this->_parseRawQueryString($value);
-                        break;
-
-                    case 'fragment':
-                        $this->anchor = $value;
-                        break;
-                }
-            }
-        }
-    }
-
-    /**
-    * Returns full url
-    *
-    * @return string Full url
-    * @access public
-    */
-    function getURL()
-    {
-        $querystring = $this->getQueryString();
-
-        $this->url = $this->protocol . '://'
-                   . $this->user . (!empty($this->pass) ? ':' : '')
-                   . $this->pass . (!empty($this->user) ? '@' : '')
-                   . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
-                   . $this->path
-                   . (!empty($querystring) ? '?' . $querystring : '')
-                   . (!empty($this->anchor) ? '#' . $this->anchor : '');
-
-        return $this->url;
-    }
-
-    /**
-    * Adds a querystring item
-    *
-    * @param  string $name       Name of item
-    * @param  string $value      Value of item
-    * @param  bool   $preencoded Whether value is urlencoded or not, default = not
-    * @access public
-    */
-    function addQueryString($name, $value, $preencoded = false)
-    {
-        if ($preencoded) {
-            $this->querystring[$name] = $value;
-        } else {
-            $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
-        }
-    }
-
-    /**
-    * Removes a querystring item
-    *
-    * @param  string $name Name of item
-    * @access public
-    */
-    function removeQueryString($name)
-    {
-        if (isset($this->querystring[$name])) {
-            unset($this->querystring[$name]);
-        }
-    }
-
-    /**
-    * Sets the querystring to literally what you supply
-    *
-    * @param  string $querystring The querystring data. Should be of the format foo=bar&x=y etc
-    * @access public
-    */
-    function addRawQueryString($querystring)
-    {
-        $this->querystring = $this->_parseRawQueryString($querystring);
-    }
-
-    /**
-    * Returns flat querystring
-    *
-    * @return string Querystring
-    * @access public
-    */
-    function getQueryString()
-    {
-        if (!empty($this->querystring)) {
-            foreach ($this->querystring as $name => $value) {
-                if (is_array($value)) {
-                    foreach ($value as $k => $v) {
-                        $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
-                    }
-                } elseif (!is_null($value)) {
-                    $querystring[] = $name . '=' . $value;
-                } else {
-                    $querystring[] = $name;
-                }
-            }
-            $querystring = implode(ini_get('arg_separator.output'), $querystring);
-        } else {
-            $querystring = '';
-        }
-
-        return $querystring;
-    }
-
-    /**
-    * Parses raw querystring and returns an array of it
-    *
-    * @param  string  $querystring The querystring to parse
-    * @return array                An array of the querystring data
-    * @access private
-    */
-    function _parseRawQuerystring($querystring)
-    {
-        $parts  = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
-        $return = array();
-
-        foreach ($parts as $part) {
-            if (strpos($part, '=') !== false) {
-                $value = substr($part, strpos($part, '=') + 1);
-                $key   = substr($part, 0, strpos($part, '='));
-            } else {
-                $value = null;
-                $key   = $part;
-            }
-            if (substr($key, -2) == '[]') {
-                $key = substr($key, 0, -2);
-                if (@!is_array($return[$key])) {
-                    $return[$key]   = array();
-                    $return[$key][] = $value;
-                } else {
-                    $return[$key][] = $value;
-                }
-            } elseif (!$this->useBrackets AND !empty($return[$key])) {
-                $return[$key]   = (array)$return[$key];
-                $return[$key][] = $value;
-            } else {
-                $return[$key] = $value;
-            }
-        }
-
-        return $return;
-    }
-
-    /**
-    * Resolves //, ../ and ./ from a path and returns
-    * the result. Eg:
-    *
-    * /foo/bar/../boo.php    => /foo/boo.php
-    * /foo/bar/../../boo.php => /boo.php
-    * /foo/bar/.././/boo.php => /foo/boo.php
-    *
-    * This method can also be called statically.
-    *
-    * @param  string $url URL path to resolve
-    * @return string      The result
-    */
-    function resolvePath($path)
-    {
-        $path = explode('/', str_replace('//', '/', $path));
-
-        for ($i=0; $i<count($path); $i++) {
-            if ($path[$i] == '.') {
-                unset($path[$i]);
-                $path = array_values($path);
-                $i--;
-
-            } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
-                unset($path[$i]);
-                unset($path[$i-1]);
-                $path = array_values($path);
-                $i -= 2;
-
-            } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
-                unset($path[$i]);
-                $path = array_values($path);
-                $i--;
-
-            } else {
-                continue;
-            }
-        }
-
-        return implode('/', $path);
-    }
-
-    /**
-    * Returns the standard port number for a protocol
-    *
-    * @param  string  $scheme The protocol to lookup
-    * @return integer         Port number or NULL if no scheme matches
-    *
-    * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
-    */
-    function getStandardPort($scheme)
-    {
-        switch (strtolower($scheme)) {
-            case 'http':    return 80;
-            case 'https':   return 443;
-            case 'ftp':     return 21;
-            case 'imap':    return 143;
-            case 'imaps':   return 993;
-            case 'pop3':    return 110;
-            case 'pop3s':   return 995;
-            default:        return null;
-       }
-    }
-
-    /**
-    * Forces the URL to a particular protocol
-    *
-    * @param string  $protocol Protocol to force the URL to
-    * @param integer $port     Optional port (standard port is used by default)
-    */
-    function setProtocol($protocol, $port = null)
-    {
-        $this->protocol = $protocol;
-        $this->port = is_null($port) ? $this->getStandardPort() : $port;
-    }
-
-}
-?>
+<?php\r
+// +-----------------------------------------------------------------------+\r
+// | Copyright (c) 2002-2004, Richard Heyes                                |\r
+// | All rights reserved.                                                  |\r
+// |                                                                       |\r
+// | Redistribution and use in source and binary forms, with or without    |\r
+// | modification, are permitted provided that the following conditions    |\r
+// | are met:                                                              |\r
+// |                                                                       |\r
+// | o Redistributions of source code must retain the above copyright      |\r
+// |   notice, this list of conditions and the following disclaimer.       |\r
+// | o Redistributions in binary form must reproduce the above copyright   |\r
+// |   notice, this list of conditions and the following disclaimer in the |\r
+// |   documentation and/or other materials provided with the distribution.|\r
+// | o The names of the authors may not be used to endorse or promote      |\r
+// |   products derived from this software without specific prior written  |\r
+// |   permission.                                                         |\r
+// |                                                                       |\r
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |\r
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |\r
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |\r
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |\r
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |\r
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |\r
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |\r
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |\r
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |\r
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |\r
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |\r
+// |                                                                       |\r
+// +-----------------------------------------------------------------------+\r
+// | Author: Richard Heyes <richard at php net>                            |\r
+// +-----------------------------------------------------------------------+\r
+//\r
+// $Id: URL.php,v 1.1 2006/11/27 17:14:39 hsur Exp $\r
+//\r
+// Net_URL Class\r
+\r
+class Net_URL\r
+{\r
+    /**\r
+    * Full url\r
+    * @var string\r
+    */\r
+    var $url;\r
+\r
+    /**\r
+    * Protocol\r
+    * @var string\r
+    */\r
+    var $protocol;\r
+\r
+    /**\r
+    * Username\r
+    * @var string\r
+    */\r
+    var $username;\r
+\r
+    /**\r
+    * Password\r
+    * @var string\r
+    */\r
+    var $password;\r
+\r
+    /**\r
+    * Host\r
+    * @var string\r
+    */\r
+    var $host;\r
+\r
+    /**\r
+    * Port\r
+    * @var integer\r
+    */\r
+    var $port;\r
+\r
+    /**\r
+    * Path\r
+    * @var string\r
+    */\r
+    var $path;\r
+\r
+    /**\r
+    * Query string\r
+    * @var array\r
+    */\r
+    var $querystring;\r
+\r
+    /**\r
+    * Anchor\r
+    * @var string\r
+    */\r
+    var $anchor;\r
+\r
+    /**\r
+    * Whether to use []\r
+    * @var bool\r
+    */\r
+    var $useBrackets;\r
+\r
+    /**\r
+    * PHP4 Constructor\r
+    *\r
+    * @see __construct()\r
+    */\r
+    function Net_URL($url = null, $useBrackets = true)\r
+    {\r
+        $this->__construct($url, $useBrackets);\r
+    }\r
+\r
+    /**\r
+    * PHP5 Constructor\r
+    *\r
+    * Parses the given url and stores the various parts\r
+    * Defaults are used in certain cases\r
+    *\r
+    * @param string $url         Optional URL\r
+    * @param bool   $useBrackets Whether to use square brackets when\r
+    *                            multiple querystrings with the same name\r
+    *                            exist\r
+    */\r
+    function __construct($url = null, $useBrackets = true)\r
+    {\r
+        $HTTP_SERVER_VARS  = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];\r
+\r
+        $this->useBrackets = $useBrackets;\r
+        $this->url         = $url;\r
+        $this->user        = '';\r
+        $this->pass        = '';\r
+        $this->host        = '';\r
+        $this->port        = 80;\r
+        $this->path        = '';\r
+        $this->querystring = array();\r
+        $this->anchor      = '';\r
+\r
+        // Only use defaults if not an absolute URL given\r
+        if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {\r
+\r
+            $this->protocol    = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');\r
+\r
+            /**\r
+            * Figure out host/port\r
+            */\r
+            if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {\r
+                $host = $matches[1];\r
+                if (!empty($matches[3])) {\r
+                    $port = $matches[3];\r
+                } else {\r
+                    $port = $this->getStandardPort($this->protocol);\r
+                }\r
+            }\r
+\r
+            $this->user        = '';\r
+            $this->pass        = '';\r
+            $this->host        = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');\r
+            $this->port        = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));\r
+            $this->path        = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';\r
+            $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;\r
+            $this->anchor      = '';\r
+        }\r
+\r
+        // Parse the url and store the various parts\r
+        if (!empty($url)) {\r
+            $urlinfo = parse_url($url);\r
+\r
+            // Default querystring\r
+            $this->querystring = array();\r
+\r
+            foreach ($urlinfo as $key => $value) {\r
+                switch ($key) {\r
+                    case 'scheme':\r
+                        $this->protocol = $value;\r
+                        $this->port     = $this->getStandardPort($value);\r
+                        break;\r
+\r
+                    case 'user':\r
+                    case 'pass':\r
+                    case 'host':\r
+                    case 'port':\r
+                        $this->$key = $value;\r
+                        break;\r
+\r
+                    case 'path':\r
+                        if ($value{0} == '/') {\r
+                            $this->path = $value;\r
+                        } else {\r
+                            $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);\r
+                            $this->path = sprintf('%s/%s', $path, $value);\r
+                        }\r
+                        break;\r
+\r
+                    case 'query':\r
+                        $this->querystring = $this->_parseRawQueryString($value);\r
+                        break;\r
+\r
+                    case 'fragment':\r
+                        $this->anchor = $value;\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Returns full url\r
+    *\r
+    * @return string Full url\r
+    * @access public\r
+    */\r
+    function getURL()\r
+    {\r
+        $querystring = $this->getQueryString();\r
+\r
+        $this->url = $this->protocol . '://'\r
+                   . $this->user . (!empty($this->pass) ? ':' : '')\r
+                   . $this->pass . (!empty($this->user) ? '@' : '')\r
+                   . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)\r
+                   . $this->path\r
+                   . (!empty($querystring) ? '?' . $querystring : '')\r
+                   . (!empty($this->anchor) ? '#' . $this->anchor : '');\r
+\r
+        return $this->url;\r
+    }\r
+\r
+    /**\r
+    * Adds a querystring item\r
+    *\r
+    * @param  string $name       Name of item\r
+    * @param  string $value      Value of item\r
+    * @param  bool   $preencoded Whether value is urlencoded or not, default = not\r
+    * @access public\r
+    */\r
+    function addQueryString($name, $value, $preencoded = false)\r
+    {\r
+        if ($preencoded) {\r
+            $this->querystring[$name] = $value;\r
+        } else {\r
+            $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Removes a querystring item\r
+    *\r
+    * @param  string $name Name of item\r
+    * @access public\r
+    */\r
+    function removeQueryString($name)\r
+    {\r
+        if (isset($this->querystring[$name])) {\r
+            unset($this->querystring[$name]);\r
+        }\r
+    }\r
+\r
+    /**\r
+    * Sets the querystring to literally what you supply\r
+    *\r
+    * @param  string $querystring The querystring data. Should be of the format foo=bar&x=y etc\r
+    * @access public\r
+    */\r
+    function addRawQueryString($querystring)\r
+    {\r
+        $this->querystring = $this->_parseRawQueryString($querystring);\r
+    }\r
+\r
+    /**\r
+    * Returns flat querystring\r
+    *\r
+    * @return string Querystring\r
+    * @access public\r
+    */\r
+    function getQueryString()\r
+    {\r
+        if (!empty($this->querystring)) {\r
+            foreach ($this->querystring as $name => $value) {\r
+                if (is_array($value)) {\r
+                    foreach ($value as $k => $v) {\r
+                        $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);\r
+                    }\r
+                } elseif (!is_null($value)) {\r
+                    $querystring[] = $name . '=' . $value;\r
+                } else {\r
+                    $querystring[] = $name;\r
+                }\r
+            }\r
+            $querystring = implode(ini_get('arg_separator.output'), $querystring);\r
+        } else {\r
+            $querystring = '';\r
+        }\r
+\r
+        return $querystring;\r
+    }\r
+\r
+    /**\r
+    * Parses raw querystring and returns an array of it\r
+    *\r
+    * @param  string  $querystring The querystring to parse\r
+    * @return array                An array of the querystring data\r
+    * @access private\r
+    */\r
+    function _parseRawQuerystring($querystring)\r
+    {\r
+        $parts  = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);\r
+        $return = array();\r
+\r
+        foreach ($parts as $part) {\r
+            if (strpos($part, '=') !== false) {\r
+                $value = substr($part, strpos($part, '=') + 1);\r
+                $key   = substr($part, 0, strpos($part, '='));\r
+            } else {\r
+                $value = null;\r
+                $key   = $part;\r
+            }\r
+            if (substr($key, -2) == '[]') {\r
+                $key = substr($key, 0, -2);\r
+                if (@!is_array($return[$key])) {\r
+                    $return[$key]   = array();\r
+                    $return[$key][] = $value;\r
+                } else {\r
+                    $return[$key][] = $value;\r
+                }\r
+            } elseif (!$this->useBrackets AND !empty($return[$key])) {\r
+                $return[$key]   = (array)$return[$key];\r
+                $return[$key][] = $value;\r
+            } else {\r
+                $return[$key] = $value;\r
+            }\r
+        }\r
+\r
+        return $return;\r
+    }\r
+\r
+    /**\r
+    * Resolves //, ../ and ./ from a path and returns\r
+    * the result. Eg:\r
+    *\r
+    * /foo/bar/../boo.php    => /foo/boo.php\r
+    * /foo/bar/../../boo.php => /boo.php\r
+    * /foo/bar/.././/boo.php => /foo/boo.php\r
+    *\r
+    * This method can also be called statically.\r
+    *\r
+    * @param  string $url URL path to resolve\r
+    * @return string      The result\r
+    */\r
+    function resolvePath($path)\r
+    {\r
+        $path = explode('/', str_replace('//', '/', $path));\r
+\r
+        for ($i=0; $i<count($path); $i++) {\r
+            if ($path[$i] == '.') {\r
+                unset($path[$i]);\r
+                $path = array_values($path);\r
+                $i--;\r
+\r
+            } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {\r
+                unset($path[$i]);\r
+                unset($path[$i-1]);\r
+                $path = array_values($path);\r
+                $i -= 2;\r
+\r
+            } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {\r
+                unset($path[$i]);\r
+                $path = array_values($path);\r
+                $i--;\r
+\r
+            } else {\r
+                continue;\r
+            }\r
+        }\r
+\r
+        return implode('/', $path);\r
+    }\r
+\r
+    /**\r
+    * Returns the standard port number for a protocol\r
+    *\r
+    * @param  string  $scheme The protocol to lookup\r
+    * @return integer         Port number or NULL if no scheme matches\r
+    *\r
+    * @author Philippe Jausions <Philippe.Jausions@11abacus.com>\r
+    */\r
+    function getStandardPort($scheme)\r
+    {\r
+        switch (strtolower($scheme)) {\r
+            case 'http':    return 80;\r
+            case 'https':   return 443;\r
+            case 'ftp':     return 21;\r
+            case 'imap':    return 143;\r
+            case 'imaps':   return 993;\r
+            case 'pop3':    return 110;\r
+            case 'pop3s':   return 995;\r
+            default:        return null;\r
+       }\r
+    }\r
+\r
+    /**\r
+    * Forces the URL to a particular protocol\r
+    *\r
+    * @param string  $protocol Protocol to force the URL to\r
+    * @param integer $port     Optional port (standard port is used by default)\r
+    */\r
+    function setProtocol($protocol, $port = null)\r
+    {\r
+        $this->protocol = $protocol;\r
+        $this->port = is_null($port) ? $this->getStandardPort() : $port;\r
+    }\r
+\r
+}\r
+?>\r
index 69f55d1..19e5b5e 100644 (file)
-<?php
-/**
- * PEAR, the PHP Extension and Application Repository
- *
- * PEAR class and PEAR_Error class
- *
- * PHP versions 4 and 5
- *
- * LICENSE: This source file is subject to version 3.0 of the PHP license
- * that is available through the world-wide-web at the following URI:
- * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
- * the PHP License and are unable to obtain it through the web, please
- * send a note to license@php.net so we can mail you a copy immediately.
- *
- * @category   pear
- * @package    PEAR
- * @author     Sterling Hughes <sterling@php.net>
- * @author     Stig Bakken <ssb@php.net>
- * @author     Tomas V.V.Cox <cox@idecnet.com>
- * @author     Greg Beaver <cellog@php.net>
- * @copyright  1997-2006 The PHP Group
- * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    CVS: $Id: PEAR.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
- * @link       http://pear.php.net/package/PEAR
- * @since      File available since Release 0.1
- */
-
-/**#@+
- * ERROR constants
- */
-define('PEAR_ERROR_RETURN',     1);
-define('PEAR_ERROR_PRINT',      2);
-define('PEAR_ERROR_TRIGGER',    4);
-define('PEAR_ERROR_DIE',        8);
-define('PEAR_ERROR_CALLBACK',  16);
-/**
- * WARNING: obsolete
- * @deprecated
- */
-define('PEAR_ERROR_EXCEPTION', 32);
-/**#@-*/
-define('PEAR_ZE2', (function_exists('version_compare') &&
-                    version_compare(zend_version(), "2-dev", "ge")));
-
-if (substr(PHP_OS, 0, 3) == 'WIN') {
-    define('OS_WINDOWS', true);
-    define('OS_UNIX',    false);
-    define('PEAR_OS',    'Windows');
-} else {
-    define('OS_WINDOWS', false);
-    define('OS_UNIX',    true);
-    define('PEAR_OS',    'Unix'); // blatant assumption
-}
-
-// instant backwards compatibility
-if (!defined('PATH_SEPARATOR')) {
-    if (OS_WINDOWS) {
-        define('PATH_SEPARATOR', ';');
-    } else {
-        define('PATH_SEPARATOR', ':');
-    }
-}
-
-$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
-$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
-$GLOBALS['_PEAR_destructor_object_list'] = array();
-$GLOBALS['_PEAR_shutdown_funcs']         = array();
-$GLOBALS['_PEAR_error_handler_stack']    = array();
-
-@ini_set('track_errors', true);
-
-/**
- * Base class for other PEAR classes.  Provides rudimentary
- * emulation of destructors.
- *
- * If you want a destructor in your class, inherit PEAR and make a
- * destructor method called _yourclassname (same name as the
- * constructor, but with a "_" prefix).  Also, in your constructor you
- * have to call the PEAR constructor: $this->PEAR();.
- * The destructor method will be called without parameters.  Note that
- * at in some SAPI implementations (such as Apache), any output during
- * the request shutdown (in which destructors are called) seems to be
- * discarded.  If you need to get any debug information from your
- * destructor, use error_log(), syslog() or something similar.
- *
- * IMPORTANT! To use the emulated destructors you need to create the
- * objects by reference: $obj =& new PEAR_child;
- *
- * @category   pear
- * @package    PEAR
- * @author     Stig Bakken <ssb@php.net>
- * @author     Tomas V.V. Cox <cox@idecnet.com>
- * @author     Greg Beaver <cellog@php.net>
- * @copyright  1997-2006 The PHP Group
- * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.4.9
- * @link       http://pear.php.net/package/PEAR
- * @see        PEAR_Error
- * @since      Class available since PHP 4.0.2
- * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear
- */
-class PEAR
-{
-    // {{{ properties
-
-    /**
-     * Whether to enable internal debug messages.
-     *
-     * @var     bool
-     * @access  private
-     */
-    var $_debug = false;
-
-    /**
-     * Default error mode for this object.
-     *
-     * @var     int
-     * @access  private
-     */
-    var $_default_error_mode = null;
-
-    /**
-     * Default error options used for this object when error mode
-     * is PEAR_ERROR_TRIGGER.
-     *
-     * @var     int
-     * @access  private
-     */
-    var $_default_error_options = null;
-
-    /**
-     * Default error handler (callback) for this object, if error mode is
-     * PEAR_ERROR_CALLBACK.
-     *
-     * @var     string
-     * @access  private
-     */
-    var $_default_error_handler = '';
-
-    /**
-     * Which class to use for error objects.
-     *
-     * @var     string
-     * @access  private
-     */
-    var $_error_class = 'PEAR_Error';
-
-    /**
-     * An array of expected errors.
-     *
-     * @var     array
-     * @access  private
-     */
-    var $_expected_errors = array();
-
-    // }}}
-
-    // {{{ constructor
-
-    /**
-     * Constructor.  Registers this object in
-     * $_PEAR_destructor_object_list for destructor emulation if a
-     * destructor object exists.
-     *
-     * @param string $error_class  (optional) which class to use for
-     *        error objects, defaults to PEAR_Error.
-     * @access public
-     * @return void
-     */
-    function PEAR($error_class = null)
-    {
-        $classname = strtolower(get_class($this));
-        if ($this->_debug) {
-            print "PEAR constructor called, class=$classname\n";
-        }
-        if ($error_class !== null) {
-            $this->_error_class = $error_class;
-        }
-        while ($classname && strcasecmp($classname, "pear")) {
-            $destructor = "_$classname";
-            if (method_exists($this, $destructor)) {
-                global $_PEAR_destructor_object_list;
-                $_PEAR_destructor_object_list[] = &$this;
-                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
-                    register_shutdown_function("_PEAR_call_destructors");
-                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
-                }
-                break;
-            } else {
-                $classname = get_parent_class($classname);
-            }
-        }
-    }
-
-    // }}}
-    // {{{ destructor
-
-    /**
-     * Destructor (the emulated type of...).  Does nothing right now,
-     * but is included for forward compatibility, so subclass
-     * destructors should always call it.
-     *
-     * See the note in the class desciption about output from
-     * destructors.
-     *
-     * @access public
-     * @return void
-     */
-    function _PEAR() {
-        if ($this->_debug) {
-            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
-        }
-    }
-
-    // }}}
-    // {{{ getStaticProperty()
-
-    /**
-    * If you have a class that's mostly/entirely static, and you need static
-    * properties, you can use this method to simulate them. Eg. in your method(s)
-    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
-    * You MUST use a reference, or they will not persist!
-    *
-    * @access public
-    * @param  string $class  The calling classname, to prevent clashes
-    * @param  string $var    The variable to retrieve.
-    * @return mixed   A reference to the variable. If not set it will be
-    *                 auto initialised to NULL.
-    */
-    function &getStaticProperty($class, $var)
-    {
-        static $properties;
-        return $properties[$class][$var];
-    }
-
-    // }}}
-    // {{{ registerShutdownFunc()
-
-    /**
-    * Use this function to register a shutdown method for static
-    * classes.
-    *
-    * @access public
-    * @param  mixed $func  The function name (or array of class/method) to call
-    * @param  mixed $args  The arguments to pass to the function
-    * @return void
-    */
-    function registerShutdownFunc($func, $args = array())
-    {
-        // if we are called statically, there is a potential
-        // that no shutdown func is registered.  Bug #6445
-        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
-            register_shutdown_function("_PEAR_call_destructors");
-            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
-        }
-        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
-    }
-
-    // }}}
-    // {{{ isError()
-
-    /**
-     * Tell whether a value is a PEAR error.
-     *
-     * @param   mixed $data   the value to test
-     * @param   int   $code   if $data is an error object, return true
-     *                        only if $code is a string and
-     *                        $obj->getMessage() == $code or
-     *                        $code is an integer and $obj->getCode() == $code
-     * @access  public
-     * @return  bool    true if parameter is an error
-     */
-    function isError($data, $code = null)
-    {
-        if (is_a($data, 'PEAR_Error')) {
-            if (is_null($code)) {
-                return true;
-            } elseif (is_string($code)) {
-                return $data->getMessage() == $code;
-            } else {
-                return $data->getCode() == $code;
-            }
-        }
-        return false;
-    }
-
-    // }}}
-    // {{{ setErrorHandling()
-
-    /**
-     * Sets how errors generated by this object should be handled.
-     * Can be invoked both in objects and statically.  If called
-     * statically, setErrorHandling sets the default behaviour for all
-     * PEAR objects.  If called in an object, setErrorHandling sets
-     * the default behaviour for that object.
-     *
-     * @param int $mode
-     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
-     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
-     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
-     *
-     * @param mixed $options
-     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
-     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
-     *
-     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
-     *        to be the callback function or method.  A callback
-     *        function is a string with the name of the function, a
-     *        callback method is an array of two elements: the element
-     *        at index 0 is the object, and the element at index 1 is
-     *        the name of the method to call in the object.
-     *
-     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
-     *        a printf format string used when printing the error
-     *        message.
-     *
-     * @access public
-     * @return void
-     * @see PEAR_ERROR_RETURN
-     * @see PEAR_ERROR_PRINT
-     * @see PEAR_ERROR_TRIGGER
-     * @see PEAR_ERROR_DIE
-     * @see PEAR_ERROR_CALLBACK
-     * @see PEAR_ERROR_EXCEPTION
-     *
-     * @since PHP 4.0.5
-     */
-
-    function setErrorHandling($mode = null, $options = null)
-    {
-        if (isset($this) && is_a($this, 'PEAR')) {
-            $setmode     = &$this->_default_error_mode;
-            $setoptions  = &$this->_default_error_options;
-        } else {
-            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
-            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
-        }
-
-        switch ($mode) {
-            case PEAR_ERROR_EXCEPTION:
-            case PEAR_ERROR_RETURN:
-            case PEAR_ERROR_PRINT:
-            case PEAR_ERROR_TRIGGER:
-            case PEAR_ERROR_DIE:
-            case null:
-                $setmode = $mode;
-                $setoptions = $options;
-                break;
-
-            case PEAR_ERROR_CALLBACK:
-                $setmode = $mode;
-                // class/object method callback
-                if (is_callable($options)) {
-                    $setoptions = $options;
-                } else {
-                    trigger_error("invalid error callback", E_USER_WARNING);
-                }
-                break;
-
-            default:
-                trigger_error("invalid error mode", E_USER_WARNING);
-                break;
-        }
-    }
-
-    // }}}
-    // {{{ expectError()
-
-    /**
-     * This method is used to tell which errors you expect to get.
-     * Expected errors are always returned with error mode
-     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
-     * and this method pushes a new element onto it.  The list of
-     * expected errors are in effect until they are popped off the
-     * stack with the popExpect() method.
-     *
-     * Note that this method can not be called statically
-     *
-     * @param mixed $code a single error code or an array of error codes to expect
-     *
-     * @return int     the new depth of the "expected errors" stack
-     * @access public
-     */
-    function expectError($code = '*')
-    {
-        if (is_array($code)) {
-            array_push($this->_expected_errors, $code);
-        } else {
-            array_push($this->_expected_errors, array($code));
-        }
-        return sizeof($this->_expected_errors);
-    }
-
-    // }}}
-    // {{{ popExpect()
-
-    /**
-     * This method pops one element off the expected error codes
-     * stack.
-     *
-     * @return array   the list of error codes that were popped
-     */
-    function popExpect()
-    {
-        return array_pop($this->_expected_errors);
-    }
-
-    // }}}
-    // {{{ _checkDelExpect()
-
-    /**
-     * This method checks unsets an error code if available
-     *
-     * @param mixed error code
-     * @return bool true if the error code was unset, false otherwise
-     * @access private
-     * @since PHP 4.3.0
-     */
-    function _checkDelExpect($error_code)
-    {
-        $deleted = false;
-
-        foreach ($this->_expected_errors AS $key => $error_array) {
-            if (in_array($error_code, $error_array)) {
-                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
-                $deleted = true;
-            }
-
-            // clean up empty arrays
-            if (0 == count($this->_expected_errors[$key])) {
-                unset($this->_expected_errors[$key]);
-            }
-        }
-        return $deleted;
-    }
-
-    // }}}
-    // {{{ delExpect()
-
-    /**
-     * This method deletes all occurences of the specified element from
-     * the expected error codes stack.
-     *
-     * @param  mixed $error_code error code that should be deleted
-     * @return mixed list of error codes that were deleted or error
-     * @access public
-     * @since PHP 4.3.0
-     */
-    function delExpect($error_code)
-    {
-        $deleted = false;
-
-        if ((is_array($error_code) && (0 != count($error_code)))) {
-            // $error_code is a non-empty array here;
-            // we walk through it trying to unset all
-            // values
-            foreach($error_code as $key => $error) {
-                if ($this->_checkDelExpect($error)) {
-                    $deleted =  true;
-                } else {
-                    $deleted = false;
-                }
-            }
-            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
-        } elseif (!empty($error_code)) {
-            // $error_code comes alone, trying to unset it
-            if ($this->_checkDelExpect($error_code)) {
-                return true;
-            } else {
-                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
-            }
-        } else {
-            // $error_code is empty
-            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
-        }
-    }
-
-    // }}}
-    // {{{ raiseError()
-
-    /**
-     * This method is a wrapper that returns an instance of the
-     * configured error class with this object's default error
-     * handling applied.  If the $mode and $options parameters are not
-     * specified, the object's defaults are used.
-     *
-     * @param mixed $message a text error message or a PEAR error object
-     *
-     * @param int $code      a numeric error code (it is up to your class
-     *                  to define these if you want to use codes)
-     *
-     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
-     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
-     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
-     *
-     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
-     *                  specifies the PHP-internal error level (one of
-     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
-     *                  If $mode is PEAR_ERROR_CALLBACK, this
-     *                  parameter specifies the callback function or
-     *                  method.  In other error modes this parameter
-     *                  is ignored.
-     *
-     * @param string $userinfo If you need to pass along for example debug
-     *                  information, this parameter is meant for that.
-     *
-     * @param string $error_class The returned error object will be
-     *                  instantiated from this class, if specified.
-     *
-     * @param bool $skipmsg If true, raiseError will only pass error codes,
-     *                  the error message parameter will be dropped.
-     *
-     * @access public
-     * @return object   a PEAR error object
-     * @see PEAR::setErrorHandling
-     * @since PHP 4.0.5
-     */
-    function &raiseError($message = null,
-                         $code = null,
-                         $mode = null,
-                         $options = null,
-                         $userinfo = null,
-                         $error_class = null,
-                         $skipmsg = false)
-    {
-        // The error is yet a PEAR error object
-        if (is_object($message)) {
-            $code        = $message->getCode();
-            $userinfo    = $message->getUserInfo();
-            $error_class = $message->getType();
-            $message->error_message_prefix = '';
-            $message     = $message->getMessage();
-        }
-
-        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
-            if ($exp[0] == "*" ||
-                (is_int(reset($exp)) && in_array($code, $exp)) ||
-                (is_string(reset($exp)) && in_array($message, $exp))) {
-                $mode = PEAR_ERROR_RETURN;
-            }
-        }
-        // No mode given, try global ones
-        if ($mode === null) {
-            // Class error handler
-            if (isset($this) && isset($this->_default_error_mode)) {
-                $mode    = $this->_default_error_mode;
-                $options = $this->_default_error_options;
-            // Global error handler
-            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
-                $mode    = $GLOBALS['_PEAR_default_error_mode'];
-                $options = $GLOBALS['_PEAR_default_error_options'];
-            }
-        }
-
-        if ($error_class !== null) {
-            $ec = $error_class;
-        } elseif (isset($this) && isset($this->_error_class)) {
-            $ec = $this->_error_class;
-        } else {
-            $ec = 'PEAR_Error';
-        }
-        if ($skipmsg) {
-            $a = &new $ec($code, $mode, $options, $userinfo);
-            return $a;
-        } else {
-            $a = &new $ec($message, $code, $mode, $options, $userinfo);
-            return $a;
-        }
-    }
-
-    // }}}
-    // {{{ throwError()
-
-    /**
-     * Simpler form of raiseError with fewer options.  In most cases
-     * message, code and userinfo are enough.
-     *
-     * @param string $message
-     *
-     */
-    function &throwError($message = null,
-                         $code = null,
-                         $userinfo = null)
-    {
-        if (isset($this) && is_a($this, 'PEAR')) {
-            $a = &$this->raiseError($message, $code, null, null, $userinfo);
-            return $a;
-        } else {
-            $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
-            return $a;
-        }
-    }
-
-    // }}}
-    function staticPushErrorHandling($mode, $options = null)
-    {
-        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
-        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
-        $def_options = &$GLOBALS['_PEAR_default_error_options'];
-        $stack[] = array($def_mode, $def_options);
-        switch ($mode) {
-            case PEAR_ERROR_EXCEPTION:
-            case PEAR_ERROR_RETURN:
-            case PEAR_ERROR_PRINT:
-            case PEAR_ERROR_TRIGGER:
-            case PEAR_ERROR_DIE:
-            case null:
-                $def_mode = $mode;
-                $def_options = $options;
-                break;
-
-            case PEAR_ERROR_CALLBACK:
-                $def_mode = $mode;
-                // class/object method callback
-                if (is_callable($options)) {
-                    $def_options = $options;
-                } else {
-                    trigger_error("invalid error callback", E_USER_WARNING);
-                }
-                break;
-
-            default:
-                trigger_error("invalid error mode", E_USER_WARNING);
-                break;
-        }
-        $stack[] = array($mode, $options);
-        return true;
-    }
-
-    function staticPopErrorHandling()
-    {
-        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
-        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
-        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
-        array_pop($stack);
-        list($mode, $options) = $stack[sizeof($stack) - 1];
-        array_pop($stack);
-        switch ($mode) {
-            case PEAR_ERROR_EXCEPTION:
-            case PEAR_ERROR_RETURN:
-            case PEAR_ERROR_PRINT:
-            case PEAR_ERROR_TRIGGER:
-            case PEAR_ERROR_DIE:
-            case null:
-                $setmode = $mode;
-                $setoptions = $options;
-                break;
-
-            case PEAR_ERROR_CALLBACK:
-                $setmode = $mode;
-                // class/object method callback
-                if (is_callable($options)) {
-                    $setoptions = $options;
-                } else {
-                    trigger_error("invalid error callback", E_USER_WARNING);
-                }
-                break;
-
-            default:
-                trigger_error("invalid error mode", E_USER_WARNING);
-                break;
-        }
-        return true;
-    }
-
-    // {{{ pushErrorHandling()
-
-    /**
-     * Push a new error handler on top of the error handler options stack. With this
-     * you can easily override the actual error handler for some code and restore
-     * it later with popErrorHandling.
-     *
-     * @param mixed $mode (same as setErrorHandling)
-     * @param mixed $options (same as setErrorHandling)
-     *
-     * @return bool Always true
-     *
-     * @see PEAR::setErrorHandling
-     */
-    function pushErrorHandling($mode, $options = null)
-    {
-        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
-        if (isset($this) && is_a($this, 'PEAR')) {
-            $def_mode    = &$this->_default_error_mode;
-            $def_options = &$this->_default_error_options;
-        } else {
-            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
-            $def_options = &$GLOBALS['_PEAR_default_error_options'];
-        }
-        $stack[] = array($def_mode, $def_options);
-
-        if (isset($this) && is_a($this, 'PEAR')) {
-            $this->setErrorHandling($mode, $options);
-        } else {
-            PEAR::setErrorHandling($mode, $options);
-        }
-        $stack[] = array($mode, $options);
-        return true;
-    }
-
-    // }}}
-    // {{{ popErrorHandling()
-
-    /**
-    * Pop the last error handler used
-    *
-    * @return bool Always true
-    *
-    * @see PEAR::pushErrorHandling
-    */
-    function popErrorHandling()
-    {
-        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
-        array_pop($stack);
-        list($mode, $options) = $stack[sizeof($stack) - 1];
-        array_pop($stack);
-        if (isset($this) && is_a($this, 'PEAR')) {
-            $this->setErrorHandling($mode, $options);
-        } else {
-            PEAR::setErrorHandling($mode, $options);
-        }
-        return true;
-    }
-
-    // }}}
-    // {{{ loadExtension()
-
-    /**
-    * OS independant PHP extension load. Remember to take care
-    * on the correct extension name for case sensitive OSes.
-    *
-    * @param string $ext The extension name
-    * @return bool Success or not on the dl() call
-    */
-    function loadExtension($ext)
-    {
-        if (!extension_loaded($ext)) {
-            // if either returns true dl() will produce a FATAL error, stop that
-            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
-                return false;
-            }
-            if (OS_WINDOWS) {
-                $suffix = '.dll';
-            } elseif (PHP_OS == 'HP-UX') {
-                $suffix = '.sl';
-            } elseif (PHP_OS == 'AIX') {
-                $suffix = '.a';
-            } elseif (PHP_OS == 'OSX') {
-                $suffix = '.bundle';
-            } else {
-                $suffix = '.so';
-            }
-            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
-        }
-        return true;
-    }
-
-    // }}}
-}
-
-// {{{ _PEAR_call_destructors()
-
-function _PEAR_call_destructors()
-{
-    global $_PEAR_destructor_object_list;
-    if (is_array($_PEAR_destructor_object_list) &&
-        sizeof($_PEAR_destructor_object_list))
-    {
-        reset($_PEAR_destructor_object_list);
-        if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
-            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
-        }
-        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
-            $classname = get_class($objref);
-            while ($classname) {
-                $destructor = "_$classname";
-                if (method_exists($objref, $destructor)) {
-                    $objref->$destructor();
-                    break;
-                } else {
-                    $classname = get_parent_class($classname);
-                }
-            }
-        }
-        // Empty the object list to ensure that destructors are
-        // not called more than once.
-        $_PEAR_destructor_object_list = array();
-    }
-
-    // Now call the shutdown functions
-    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
-        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
-            call_user_func_array($value[0], $value[1]);
-        }
-    }
-}
-
-// }}}
-/**
- * Standard PEAR error class for PHP 4
- *
- * This class is supserseded by {@link PEAR_Exception} in PHP 5
- *
- * @category   pear
- * @package    PEAR
- * @author     Stig Bakken <ssb@php.net>
- * @author     Tomas V.V. Cox <cox@idecnet.com>
- * @author     Gregory Beaver <cellog@php.net>
- * @copyright  1997-2006 The PHP Group
- * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
- * @version    Release: 1.4.9
- * @link       http://pear.php.net/manual/en/core.pear.pear-error.php
- * @see        PEAR::raiseError(), PEAR::throwError()
- * @since      Class available since PHP 4.0.2
- */
-class PEAR_Error
-{
-    // {{{ properties
-
-    var $error_message_prefix = '';
-    var $mode                 = PEAR_ERROR_RETURN;
-    var $level                = E_USER_NOTICE;
-    var $code                 = -1;
-    var $message              = '';
-    var $userinfo             = '';
-    var $backtrace            = null;
-
-    // }}}
-    // {{{ constructor
-
-    /**
-     * PEAR_Error constructor
-     *
-     * @param string $message  message
-     *
-     * @param int $code     (optional) error code
-     *
-     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
-     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
-     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
-     *
-     * @param mixed $options   (optional) error level, _OR_ in the case of
-     * PEAR_ERROR_CALLBACK, the callback function or object/method
-     * tuple.
-     *
-     * @param string $userinfo (optional) additional user/debug info
-     *
-     * @access public
-     *
-     */
-    function PEAR_Error($message = 'unknown error', $code = null,
-                        $mode = null, $options = null, $userinfo = null)
-    {
-        if ($mode === null) {
-            $mode = PEAR_ERROR_RETURN;
-        }
-        $this->message   = $message;
-        $this->code      = $code;
-        $this->mode      = $mode;
-        $this->userinfo  = $userinfo;
-        if (function_exists("debug_backtrace")) {
-            if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
-                $this->backtrace = debug_backtrace();
-            }
-        }
-        if ($mode & PEAR_ERROR_CALLBACK) {
-            $this->level = E_USER_NOTICE;
-            $this->callback = $options;
-        } else {
-            if ($options === null) {
-                $options = E_USER_NOTICE;
-            }
-            $this->level = $options;
-            $this->callback = null;
-        }
-        if ($this->mode & PEAR_ERROR_PRINT) {
-            if (is_null($options) || is_int($options)) {
-                $format = "%s";
-            } else {
-                $format = $options;
-            }
-            printf($format, $this->getMessage());
-        }
-        if ($this->mode & PEAR_ERROR_TRIGGER) {
-            trigger_error($this->getMessage(), $this->level);
-        }
-        if ($this->mode & PEAR_ERROR_DIE) {
-            $msg = $this->getMessage();
-            if (is_null($options) || is_int($options)) {
-                $format = "%s";
-                if (substr($msg, -1) != "\n") {
-                    $msg .= "\n";
-                }
-            } else {
-                $format = $options;
-            }
-            die(sprintf($format, $msg));
-        }
-        if ($this->mode & PEAR_ERROR_CALLBACK) {
-            if (is_callable($this->callback)) {
-                call_user_func($this->callback, $this);
-            }
-        }
-        if ($this->mode & PEAR_ERROR_EXCEPTION) {
-            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
-            eval('$e = new Exception($this->message, $this->code);throw($e);');
-        }
-    }
-
-    // }}}
-    // {{{ getMode()
-
-    /**
-     * Get the error mode from an error object.
-     *
-     * @return int error mode
-     * @access public
-     */
-    function getMode() {
-        return $this->mode;
-    }
-
-    // }}}
-    // {{{ getCallback()
-
-    /**
-     * Get the callback function/method from an error object.
-     *
-     * @return mixed callback function or object/method array
-     * @access public
-     */
-    function getCallback() {
-        return $this->callback;
-    }
-
-    // }}}
-    // {{{ getMessage()
-
-
-    /**
-     * Get the error message from an error object.
-     *
-     * @return  string  full error message
-     * @access public
-     */
-    function getMessage()
-    {
-        return ($this->error_message_prefix . $this->message);
-    }
-
-
-    // }}}
-    // {{{ getCode()
-
-    /**
-     * Get error code from an error object
-     *
-     * @return int error code
-     * @access public
-     */
-     function getCode()
-     {
-        return $this->code;
-     }
-
-    // }}}
-    // {{{ getType()
-
-    /**
-     * Get the name of this error/exception.
-     *
-     * @return string error/exception name (type)
-     * @access public
-     */
-    function getType()
-    {
-        return get_class($this);
-    }
-
-    // }}}
-    // {{{ getUserInfo()
-
-    /**
-     * Get additional user-supplied information.
-     *
-     * @return string user-supplied information
-     * @access public
-     */
-    function getUserInfo()
-    {
-        return $this->userinfo;
-    }
-
-    // }}}
-    // {{{ getDebugInfo()
-
-    /**
-     * Get additional debug information supplied by the application.
-     *
-     * @return string debug information
-     * @access public
-     */
-    function getDebugInfo()
-    {
-        return $this->getUserInfo();
-    }
-
-    // }}}
-    // {{{ getBacktrace()
-
-    /**
-     * Get the call backtrace from where the error was generated.
-     * Supported with PHP 4.3.0 or newer.
-     *
-     * @param int $frame (optional) what frame to fetch
-     * @return array Backtrace, or NULL if not available.
-     * @access public
-     */
-    function getBacktrace($frame = null)
-    {
-        if (defined('PEAR_IGNORE_BACKTRACE')) {
-            return null;
-        }
-        if ($frame === null) {
-            return $this->backtrace;
-        }
-        return $this->backtrace[$frame];
-    }
-
-    // }}}
-    // {{{ addUserInfo()
-
-    function addUserInfo($info)
-    {
-        if (empty($this->userinfo)) {
-            $this->userinfo = $info;
-        } else {
-            $this->userinfo .= " ** $info";
-        }
-    }
-
-    // }}}
-    // {{{ toString()
-
-    /**
-     * Make a string representation of this object.
-     *
-     * @return string a string with an object summary
-     * @access public
-     */
-    function toString() {
-        $modes = array();
-        $levels = array(E_USER_NOTICE  => 'notice',
-                        E_USER_WARNING => 'warning',
-                        E_USER_ERROR   => 'error');
-        if ($this->mode & PEAR_ERROR_CALLBACK) {
-            if (is_array($this->callback)) {
-                $callback = (is_object($this->callback[0]) ?
-                    strtolower(get_class($this->callback[0])) :
-                    $this->callback[0]) . '::' .
-                    $this->callback[1];
-            } else {
-                $callback = $this->callback;
-            }
-            return sprintf('[%s: message="%s" code=%d mode=callback '.
-                           'callback=%s prefix="%s" info="%s"]',
-                           strtolower(get_class($this)), $this->message, $this->code,
-                           $callback, $this->error_message_prefix,
-                           $this->userinfo);
-        }
-        if ($this->mode & PEAR_ERROR_PRINT) {
-            $modes[] = 'print';
-        }
-        if ($this->mode & PEAR_ERROR_TRIGGER) {
-            $modes[] = 'trigger';
-        }
-        if ($this->mode & PEAR_ERROR_DIE) {
-            $modes[] = 'die';
-        }
-        if ($this->mode & PEAR_ERROR_RETURN) {
-            $modes[] = 'return';
-        }
-        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
-                       'prefix="%s" info="%s"]',
-                       strtolower(get_class($this)), $this->message, $this->code,
-                       implode("|", $modes), $levels[$this->level],
-                       $this->error_message_prefix,
-                       $this->userinfo);
-    }
-
-    // }}}
-}
-
-/*
- * Local Variables:
- * mode: php
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- */
-?>
+<?php\r
+/**\r
+ * PEAR, the PHP Extension and Application Repository\r
+ *\r
+ * PEAR class and PEAR_Error class\r
+ *\r
+ * PHP versions 4 and 5\r
+ *\r
+ * LICENSE: This source file is subject to version 3.0 of the PHP license\r
+ * that is available through the world-wide-web at the following URI:\r
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of\r
+ * the PHP License and are unable to obtain it through the web, please\r
+ * send a note to license@php.net so we can mail you a copy immediately.\r
+ *\r
+ * @category   pear\r
+ * @package    PEAR\r
+ * @author     Sterling Hughes <sterling@php.net>\r
+ * @author     Stig Bakken <ssb@php.net>\r
+ * @author     Tomas V.V.Cox <cox@idecnet.com>\r
+ * @author     Greg Beaver <cellog@php.net>\r
+ * @copyright  1997-2006 The PHP Group\r
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0\r
+ * @version    CVS: $Id: PEAR.php,v 1.7 2006/09/29 14:44:16 hsur Exp $\r
+ * @link       http://pear.php.net/package/PEAR\r
+ * @since      File available since Release 0.1\r
+ */\r
+\r
+/**#@+\r
+ * ERROR constants\r
+ */\r
+define('PEAR_ERROR_RETURN',     1);\r
+define('PEAR_ERROR_PRINT',      2);\r
+define('PEAR_ERROR_TRIGGER',    4);\r
+define('PEAR_ERROR_DIE',        8);\r
+define('PEAR_ERROR_CALLBACK',  16);\r
+/**\r
+ * WARNING: obsolete\r
+ * @deprecated\r
+ */\r
+define('PEAR_ERROR_EXCEPTION', 32);\r
+/**#@-*/\r
+define('PEAR_ZE2', (function_exists('version_compare') &&\r
+                    version_compare(zend_version(), "2-dev", "ge")));\r
+\r
+if (substr(PHP_OS, 0, 3) == 'WIN') {\r
+    define('OS_WINDOWS', true);\r
+    define('OS_UNIX',    false);\r
+    define('PEAR_OS',    'Windows');\r
+} else {\r
+    define('OS_WINDOWS', false);\r
+    define('OS_UNIX',    true);\r
+    define('PEAR_OS',    'Unix'); // blatant assumption\r
+}\r
+\r
+// instant backwards compatibility\r
+if (!defined('PATH_SEPARATOR')) {\r
+    if (OS_WINDOWS) {\r
+        define('PATH_SEPARATOR', ';');\r
+    } else {\r
+        define('PATH_SEPARATOR', ':');\r
+    }\r
+}\r
+\r
+$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;\r
+$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;\r
+$GLOBALS['_PEAR_destructor_object_list'] = array();\r
+$GLOBALS['_PEAR_shutdown_funcs']         = array();\r
+$GLOBALS['_PEAR_error_handler_stack']    = array();\r
+\r
+@ini_set('track_errors', true);\r
+\r
+/**\r
+ * Base class for other PEAR classes.  Provides rudimentary\r
+ * emulation of destructors.\r
+ *\r
+ * If you want a destructor in your class, inherit PEAR and make a\r
+ * destructor method called _yourclassname (same name as the\r
+ * constructor, but with a "_" prefix).  Also, in your constructor you\r
+ * have to call the PEAR constructor: $this->PEAR();.\r
+ * The destructor method will be called without parameters.  Note that\r
+ * at in some SAPI implementations (such as Apache), any output during\r
+ * the request shutdown (in which destructors are called) seems to be\r
+ * discarded.  If you need to get any debug information from your\r
+ * destructor, use error_log(), syslog() or something similar.\r
+ *\r
+ * IMPORTANT! To use the emulated destructors you need to create the\r
+ * objects by reference: $obj =& new PEAR_child;\r
+ *\r
+ * @category   pear\r
+ * @package    PEAR\r
+ * @author     Stig Bakken <ssb@php.net>\r
+ * @author     Tomas V.V. Cox <cox@idecnet.com>\r
+ * @author     Greg Beaver <cellog@php.net>\r
+ * @copyright  1997-2006 The PHP Group\r
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0\r
+ * @version    Release: 1.4.9\r
+ * @link       http://pear.php.net/package/PEAR\r
+ * @see        PEAR_Error\r
+ * @since      Class available since PHP 4.0.2\r
+ * @link        http://pear.php.net/manual/en/core.pear.php#core.pear.pear\r
+ */\r
+class PEAR\r
+{\r
+    // {{{ properties\r
+\r
+    /**\r
+     * Whether to enable internal debug messages.\r
+     *\r
+     * @var     bool\r
+     * @access  private\r
+     */\r
+    var $_debug = false;\r
+\r
+    /**\r
+     * Default error mode for this object.\r
+     *\r
+     * @var     int\r
+     * @access  private\r
+     */\r
+    var $_default_error_mode = null;\r
+\r
+    /**\r
+     * Default error options used for this object when error mode\r
+     * is PEAR_ERROR_TRIGGER.\r
+     *\r
+     * @var     int\r
+     * @access  private\r
+     */\r
+    var $_default_error_options = null;\r
+\r
+    /**\r
+     * Default error handler (callback) for this object, if error mode is\r
+     * PEAR_ERROR_CALLBACK.\r
+     *\r
+     * @var     string\r
+     * @access  private\r
+     */\r
+    var $_default_error_handler = '';\r
+\r
+    /**\r
+     * Which class to use for error objects.\r
+     *\r
+     * @var     string\r
+     * @access  private\r
+     */\r
+    var $_error_class = 'PEAR_Error';\r
+\r
+    /**\r
+     * An array of expected errors.\r
+     *\r
+     * @var     array\r
+     * @access  private\r
+     */\r
+    var $_expected_errors = array();\r
+\r
+    // }}}\r
+\r
+    // {{{ constructor\r
+\r
+    /**\r
+     * Constructor.  Registers this object in\r
+     * $_PEAR_destructor_object_list for destructor emulation if a\r
+     * destructor object exists.\r
+     *\r
+     * @param string $error_class  (optional) which class to use for\r
+     *        error objects, defaults to PEAR_Error.\r
+     * @access public\r
+     * @return void\r
+     */\r
+    function PEAR($error_class = null)\r
+    {\r
+        $classname = strtolower(get_class($this));\r
+        if ($this->_debug) {\r
+            print "PEAR constructor called, class=$classname\n";\r
+        }\r
+        if ($error_class !== null) {\r
+            $this->_error_class = $error_class;\r
+        }\r
+        while ($classname && strcasecmp($classname, "pear")) {\r
+            $destructor = "_$classname";\r
+            if (method_exists($this, $destructor)) {\r
+                global $_PEAR_destructor_object_list;\r
+                $_PEAR_destructor_object_list[] = &$this;\r
+                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {\r
+                    register_shutdown_function("_PEAR_call_destructors");\r
+                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;\r
+                }\r
+                break;\r
+            } else {\r
+                $classname = get_parent_class($classname);\r
+            }\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ destructor\r
+\r
+    /**\r
+     * Destructor (the emulated type of...).  Does nothing right now,\r
+     * but is included for forward compatibility, so subclass\r
+     * destructors should always call it.\r
+     *\r
+     * See the note in the class desciption about output from\r
+     * destructors.\r
+     *\r
+     * @access public\r
+     * @return void\r
+     */\r
+    function _PEAR() {\r
+        if ($this->_debug) {\r
+            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getStaticProperty()\r
+\r
+    /**\r
+    * If you have a class that's mostly/entirely static, and you need static\r
+    * properties, you can use this method to simulate them. Eg. in your method(s)\r
+    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');\r
+    * You MUST use a reference, or they will not persist!\r
+    *\r
+    * @access public\r
+    * @param  string $class  The calling classname, to prevent clashes\r
+    * @param  string $var    The variable to retrieve.\r
+    * @return mixed   A reference to the variable. If not set it will be\r
+    *                 auto initialised to NULL.\r
+    */\r
+    function &getStaticProperty($class, $var)\r
+    {\r
+        static $properties;\r
+        return $properties[$class][$var];\r
+    }\r
+\r
+    // }}}\r
+    // {{{ registerShutdownFunc()\r
+\r
+    /**\r
+    * Use this function to register a shutdown method for static\r
+    * classes.\r
+    *\r
+    * @access public\r
+    * @param  mixed $func  The function name (or array of class/method) to call\r
+    * @param  mixed $args  The arguments to pass to the function\r
+    * @return void\r
+    */\r
+    function registerShutdownFunc($func, $args = array())\r
+    {\r
+        // if we are called statically, there is a potential\r
+        // that no shutdown func is registered.  Bug #6445\r
+        if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {\r
+            register_shutdown_function("_PEAR_call_destructors");\r
+            $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;\r
+        }\r
+        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);\r
+    }\r
+\r
+    // }}}\r
+    // {{{ isError()\r
+\r
+    /**\r
+     * Tell whether a value is a PEAR error.\r
+     *\r
+     * @param   mixed $data   the value to test\r
+     * @param   int   $code   if $data is an error object, return true\r
+     *                        only if $code is a string and\r
+     *                        $obj->getMessage() == $code or\r
+     *                        $code is an integer and $obj->getCode() == $code\r
+     * @access  public\r
+     * @return  bool    true if parameter is an error\r
+     */\r
+    function isError($data, $code = null)\r
+    {\r
+        if (is_a($data, 'PEAR_Error')) {\r
+            if (is_null($code)) {\r
+                return true;\r
+            } elseif (is_string($code)) {\r
+                return $data->getMessage() == $code;\r
+            } else {\r
+                return $data->getCode() == $code;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ setErrorHandling()\r
+\r
+    /**\r
+     * Sets how errors generated by this object should be handled.\r
+     * Can be invoked both in objects and statically.  If called\r
+     * statically, setErrorHandling sets the default behaviour for all\r
+     * PEAR objects.  If called in an object, setErrorHandling sets\r
+     * the default behaviour for that object.\r
+     *\r
+     * @param int $mode\r
+     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,\r
+     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,\r
+     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.\r
+     *\r
+     * @param mixed $options\r
+     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one\r
+     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).\r
+     *\r
+     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected\r
+     *        to be the callback function or method.  A callback\r
+     *        function is a string with the name of the function, a\r
+     *        callback method is an array of two elements: the element\r
+     *        at index 0 is the object, and the element at index 1 is\r
+     *        the name of the method to call in the object.\r
+     *\r
+     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is\r
+     *        a printf format string used when printing the error\r
+     *        message.\r
+     *\r
+     * @access public\r
+     * @return void\r
+     * @see PEAR_ERROR_RETURN\r
+     * @see PEAR_ERROR_PRINT\r
+     * @see PEAR_ERROR_TRIGGER\r
+     * @see PEAR_ERROR_DIE\r
+     * @see PEAR_ERROR_CALLBACK\r
+     * @see PEAR_ERROR_EXCEPTION\r
+     *\r
+     * @since PHP 4.0.5\r
+     */\r
+\r
+    function setErrorHandling($mode = null, $options = null)\r
+    {\r
+        if (isset($this) && is_a($this, 'PEAR')) {\r
+            $setmode     = &$this->_default_error_mode;\r
+            $setoptions  = &$this->_default_error_options;\r
+        } else {\r
+            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];\r
+            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];\r
+        }\r
+\r
+        switch ($mode) {\r
+            case PEAR_ERROR_EXCEPTION:\r
+            case PEAR_ERROR_RETURN:\r
+            case PEAR_ERROR_PRINT:\r
+            case PEAR_ERROR_TRIGGER:\r
+            case PEAR_ERROR_DIE:\r
+            case null:\r
+                $setmode = $mode;\r
+                $setoptions = $options;\r
+                break;\r
+\r
+            case PEAR_ERROR_CALLBACK:\r
+                $setmode = $mode;\r
+                // class/object method callback\r
+                if (is_callable($options)) {\r
+                    $setoptions = $options;\r
+                } else {\r
+                    trigger_error("invalid error callback", E_USER_WARNING);\r
+                }\r
+                break;\r
+\r
+            default:\r
+                trigger_error("invalid error mode", E_USER_WARNING);\r
+                break;\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ expectError()\r
+\r
+    /**\r
+     * This method is used to tell which errors you expect to get.\r
+     * Expected errors are always returned with error mode\r
+     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,\r
+     * and this method pushes a new element onto it.  The list of\r
+     * expected errors are in effect until they are popped off the\r
+     * stack with the popExpect() method.\r
+     *\r
+     * Note that this method can not be called statically\r
+     *\r
+     * @param mixed $code a single error code or an array of error codes to expect\r
+     *\r
+     * @return int     the new depth of the "expected errors" stack\r
+     * @access public\r
+     */\r
+    function expectError($code = '*')\r
+    {\r
+        if (is_array($code)) {\r
+            array_push($this->_expected_errors, $code);\r
+        } else {\r
+            array_push($this->_expected_errors, array($code));\r
+        }\r
+        return sizeof($this->_expected_errors);\r
+    }\r
+\r
+    // }}}\r
+    // {{{ popExpect()\r
+\r
+    /**\r
+     * This method pops one element off the expected error codes\r
+     * stack.\r
+     *\r
+     * @return array   the list of error codes that were popped\r
+     */\r
+    function popExpect()\r
+    {\r
+        return array_pop($this->_expected_errors);\r
+    }\r
+\r
+    // }}}\r
+    // {{{ _checkDelExpect()\r
+\r
+    /**\r
+     * This method checks unsets an error code if available\r
+     *\r
+     * @param mixed error code\r
+     * @return bool true if the error code was unset, false otherwise\r
+     * @access private\r
+     * @since PHP 4.3.0\r
+     */\r
+    function _checkDelExpect($error_code)\r
+    {\r
+        $deleted = false;\r
+\r
+        foreach ($this->_expected_errors AS $key => $error_array) {\r
+            if (in_array($error_code, $error_array)) {\r
+                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);\r
+                $deleted = true;\r
+            }\r
+\r
+            // clean up empty arrays\r
+            if (0 == count($this->_expected_errors[$key])) {\r
+                unset($this->_expected_errors[$key]);\r
+            }\r
+        }\r
+        return $deleted;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ delExpect()\r
+\r
+    /**\r
+     * This method deletes all occurences of the specified element from\r
+     * the expected error codes stack.\r
+     *\r
+     * @param  mixed $error_code error code that should be deleted\r
+     * @return mixed list of error codes that were deleted or error\r
+     * @access public\r
+     * @since PHP 4.3.0\r
+     */\r
+    function delExpect($error_code)\r
+    {\r
+        $deleted = false;\r
+\r
+        if ((is_array($error_code) && (0 != count($error_code)))) {\r
+            // $error_code is a non-empty array here;\r
+            // we walk through it trying to unset all\r
+            // values\r
+            foreach($error_code as $key => $error) {\r
+                if ($this->_checkDelExpect($error)) {\r
+                    $deleted =  true;\r
+                } else {\r
+                    $deleted = false;\r
+                }\r
+            }\r
+            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME\r
+        } elseif (!empty($error_code)) {\r
+            // $error_code comes alone, trying to unset it\r
+            if ($this->_checkDelExpect($error_code)) {\r
+                return true;\r
+            } else {\r
+                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME\r
+            }\r
+        } else {\r
+            // $error_code is empty\r
+            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ raiseError()\r
+\r
+    /**\r
+     * This method is a wrapper that returns an instance of the\r
+     * configured error class with this object's default error\r
+     * handling applied.  If the $mode and $options parameters are not\r
+     * specified, the object's defaults are used.\r
+     *\r
+     * @param mixed $message a text error message or a PEAR error object\r
+     *\r
+     * @param int $code      a numeric error code (it is up to your class\r
+     *                  to define these if you want to use codes)\r
+     *\r
+     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,\r
+     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,\r
+     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.\r
+     *\r
+     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter\r
+     *                  specifies the PHP-internal error level (one of\r
+     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).\r
+     *                  If $mode is PEAR_ERROR_CALLBACK, this\r
+     *                  parameter specifies the callback function or\r
+     *                  method.  In other error modes this parameter\r
+     *                  is ignored.\r
+     *\r
+     * @param string $userinfo If you need to pass along for example debug\r
+     *                  information, this parameter is meant for that.\r
+     *\r
+     * @param string $error_class The returned error object will be\r
+     *                  instantiated from this class, if specified.\r
+     *\r
+     * @param bool $skipmsg If true, raiseError will only pass error codes,\r
+     *                  the error message parameter will be dropped.\r
+     *\r
+     * @access public\r
+     * @return object   a PEAR error object\r
+     * @see PEAR::setErrorHandling\r
+     * @since PHP 4.0.5\r
+     */\r
+    function &raiseError($message = null,\r
+                         $code = null,\r
+                         $mode = null,\r
+                         $options = null,\r
+                         $userinfo = null,\r
+                         $error_class = null,\r
+                         $skipmsg = false)\r
+    {\r
+        // The error is yet a PEAR error object\r
+        if (is_object($message)) {\r
+            $code        = $message->getCode();\r
+            $userinfo    = $message->getUserInfo();\r
+            $error_class = $message->getType();\r
+            $message->error_message_prefix = '';\r
+            $message     = $message->getMessage();\r
+        }\r
+\r
+        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {\r
+            if ($exp[0] == "*" ||\r
+                (is_int(reset($exp)) && in_array($code, $exp)) ||\r
+                (is_string(reset($exp)) && in_array($message, $exp))) {\r
+                $mode = PEAR_ERROR_RETURN;\r
+            }\r
+        }\r
+        // No mode given, try global ones\r
+        if ($mode === null) {\r
+            // Class error handler\r
+            if (isset($this) && isset($this->_default_error_mode)) {\r
+                $mode    = $this->_default_error_mode;\r
+                $options = $this->_default_error_options;\r
+            // Global error handler\r
+            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {\r
+                $mode    = $GLOBALS['_PEAR_default_error_mode'];\r
+                $options = $GLOBALS['_PEAR_default_error_options'];\r
+            }\r
+        }\r
+\r
+        if ($error_class !== null) {\r
+            $ec = $error_class;\r
+        } elseif (isset($this) && isset($this->_error_class)) {\r
+            $ec = $this->_error_class;\r
+        } else {\r
+            $ec = 'PEAR_Error';\r
+        }\r
+        if ($skipmsg) {\r
+            $a = &new $ec($code, $mode, $options, $userinfo);\r
+            return $a;\r
+        } else {\r
+            $a = &new $ec($message, $code, $mode, $options, $userinfo);\r
+            return $a;\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ throwError()\r
+\r
+    /**\r
+     * Simpler form of raiseError with fewer options.  In most cases\r
+     * message, code and userinfo are enough.\r
+     *\r
+     * @param string $message\r
+     *\r
+     */\r
+    function &throwError($message = null,\r
+                         $code = null,\r
+                         $userinfo = null)\r
+    {\r
+        if (isset($this) && is_a($this, 'PEAR')) {\r
+            $a = &$this->raiseError($message, $code, null, null, $userinfo);\r
+            return $a;\r
+        } else {\r
+            $a = &PEAR::raiseError($message, $code, null, null, $userinfo);\r
+            return $a;\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    function staticPushErrorHandling($mode, $options = null)\r
+    {\r
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];\r
+        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];\r
+        $def_options = &$GLOBALS['_PEAR_default_error_options'];\r
+        $stack[] = array($def_mode, $def_options);\r
+        switch ($mode) {\r
+            case PEAR_ERROR_EXCEPTION:\r
+            case PEAR_ERROR_RETURN:\r
+            case PEAR_ERROR_PRINT:\r
+            case PEAR_ERROR_TRIGGER:\r
+            case PEAR_ERROR_DIE:\r
+            case null:\r
+                $def_mode = $mode;\r
+                $def_options = $options;\r
+                break;\r
+\r
+            case PEAR_ERROR_CALLBACK:\r
+                $def_mode = $mode;\r
+                // class/object method callback\r
+                if (is_callable($options)) {\r
+                    $def_options = $options;\r
+                } else {\r
+                    trigger_error("invalid error callback", E_USER_WARNING);\r
+                }\r
+                break;\r
+\r
+            default:\r
+                trigger_error("invalid error mode", E_USER_WARNING);\r
+                break;\r
+        }\r
+        $stack[] = array($mode, $options);\r
+        return true;\r
+    }\r
+\r
+    function staticPopErrorHandling()\r
+    {\r
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];\r
+        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];\r
+        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];\r
+        array_pop($stack);\r
+        list($mode, $options) = $stack[sizeof($stack) - 1];\r
+        array_pop($stack);\r
+        switch ($mode) {\r
+            case PEAR_ERROR_EXCEPTION:\r
+            case PEAR_ERROR_RETURN:\r
+            case PEAR_ERROR_PRINT:\r
+            case PEAR_ERROR_TRIGGER:\r
+            case PEAR_ERROR_DIE:\r
+            case null:\r
+                $setmode = $mode;\r
+                $setoptions = $options;\r
+                break;\r
+\r
+            case PEAR_ERROR_CALLBACK:\r
+                $setmode = $mode;\r
+                // class/object method callback\r
+                if (is_callable($options)) {\r
+                    $setoptions = $options;\r
+                } else {\r
+                    trigger_error("invalid error callback", E_USER_WARNING);\r
+                }\r
+                break;\r
+\r
+            default:\r
+                trigger_error("invalid error mode", E_USER_WARNING);\r
+                break;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // {{{ pushErrorHandling()\r
+\r
+    /**\r
+     * Push a new error handler on top of the error handler options stack. With this\r
+     * you can easily override the actual error handler for some code and restore\r
+     * it later with popErrorHandling.\r
+     *\r
+     * @param mixed $mode (same as setErrorHandling)\r
+     * @param mixed $options (same as setErrorHandling)\r
+     *\r
+     * @return bool Always true\r
+     *\r
+     * @see PEAR::setErrorHandling\r
+     */\r
+    function pushErrorHandling($mode, $options = null)\r
+    {\r
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];\r
+        if (isset($this) && is_a($this, 'PEAR')) {\r
+            $def_mode    = &$this->_default_error_mode;\r
+            $def_options = &$this->_default_error_options;\r
+        } else {\r
+            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];\r
+            $def_options = &$GLOBALS['_PEAR_default_error_options'];\r
+        }\r
+        $stack[] = array($def_mode, $def_options);\r
+\r
+        if (isset($this) && is_a($this, 'PEAR')) {\r
+            $this->setErrorHandling($mode, $options);\r
+        } else {\r
+            PEAR::setErrorHandling($mode, $options);\r
+        }\r
+        $stack[] = array($mode, $options);\r
+        return true;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ popErrorHandling()\r
+\r
+    /**\r
+    * Pop the last error handler used\r
+    *\r
+    * @return bool Always true\r
+    *\r
+    * @see PEAR::pushErrorHandling\r
+    */\r
+    function popErrorHandling()\r
+    {\r
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];\r
+        array_pop($stack);\r
+        list($mode, $options) = $stack[sizeof($stack) - 1];\r
+        array_pop($stack);\r
+        if (isset($this) && is_a($this, 'PEAR')) {\r
+            $this->setErrorHandling($mode, $options);\r
+        } else {\r
+            PEAR::setErrorHandling($mode, $options);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ loadExtension()\r
+\r
+    /**\r
+    * OS independant PHP extension load. Remember to take care\r
+    * on the correct extension name for case sensitive OSes.\r
+    *\r
+    * @param string $ext The extension name\r
+    * @return bool Success or not on the dl() call\r
+    */\r
+    function loadExtension($ext)\r
+    {\r
+        if (!extension_loaded($ext)) {\r
+            // if either returns true dl() will produce a FATAL error, stop that\r
+            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {\r
+                return false;\r
+            }\r
+            if (OS_WINDOWS) {\r
+                $suffix = '.dll';\r
+            } elseif (PHP_OS == 'HP-UX') {\r
+                $suffix = '.sl';\r
+            } elseif (PHP_OS == 'AIX') {\r
+                $suffix = '.a';\r
+            } elseif (PHP_OS == 'OSX') {\r
+                $suffix = '.bundle';\r
+            } else {\r
+                $suffix = '.so';\r
+            }\r
+            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);\r
+        }\r
+        return true;\r
+    }\r
+\r
+    // }}}\r
+}\r
+\r
+// {{{ _PEAR_call_destructors()\r
+\r
+function _PEAR_call_destructors()\r
+{\r
+    global $_PEAR_destructor_object_list;\r
+    if (is_array($_PEAR_destructor_object_list) &&\r
+        sizeof($_PEAR_destructor_object_list))\r
+    {\r
+        reset($_PEAR_destructor_object_list);\r
+        if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {\r
+            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);\r
+        }\r
+        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {\r
+            $classname = get_class($objref);\r
+            while ($classname) {\r
+                $destructor = "_$classname";\r
+                if (method_exists($objref, $destructor)) {\r
+                    $objref->$destructor();\r
+                    break;\r
+                } else {\r
+                    $classname = get_parent_class($classname);\r
+                }\r
+            }\r
+        }\r
+        // Empty the object list to ensure that destructors are\r
+        // not called more than once.\r
+        $_PEAR_destructor_object_list = array();\r
+    }\r
+\r
+    // Now call the shutdown functions\r
+    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {\r
+        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {\r
+            call_user_func_array($value[0], $value[1]);\r
+        }\r
+    }\r
+}\r
+\r
+// }}}\r
+/**\r
+ * Standard PEAR error class for PHP 4\r
+ *\r
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5\r
+ *\r
+ * @category   pear\r
+ * @package    PEAR\r
+ * @author     Stig Bakken <ssb@php.net>\r
+ * @author     Tomas V.V. Cox <cox@idecnet.com>\r
+ * @author     Gregory Beaver <cellog@php.net>\r
+ * @copyright  1997-2006 The PHP Group\r
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0\r
+ * @version    Release: 1.4.9\r
+ * @link       http://pear.php.net/manual/en/core.pear.pear-error.php\r
+ * @see        PEAR::raiseError(), PEAR::throwError()\r
+ * @since      Class available since PHP 4.0.2\r
+ */\r
+class PEAR_Error\r
+{\r
+    // {{{ properties\r
+\r
+    var $error_message_prefix = '';\r
+    var $mode                 = PEAR_ERROR_RETURN;\r
+    var $level                = E_USER_NOTICE;\r
+    var $code                 = -1;\r
+    var $message              = '';\r
+    var $userinfo             = '';\r
+    var $backtrace            = null;\r
+\r
+    // }}}\r
+    // {{{ constructor\r
+\r
+    /**\r
+     * PEAR_Error constructor\r
+     *\r
+     * @param string $message  message\r
+     *\r
+     * @param int $code     (optional) error code\r
+     *\r
+     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,\r
+     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,\r
+     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION\r
+     *\r
+     * @param mixed $options   (optional) error level, _OR_ in the case of\r
+     * PEAR_ERROR_CALLBACK, the callback function or object/method\r
+     * tuple.\r
+     *\r
+     * @param string $userinfo (optional) additional user/debug info\r
+     *\r
+     * @access public\r
+     *\r
+     */\r
+    function PEAR_Error($message = 'unknown error', $code = null,\r
+                        $mode = null, $options = null, $userinfo = null)\r
+    {\r
+        if ($mode === null) {\r
+            $mode = PEAR_ERROR_RETURN;\r
+        }\r
+        $this->message   = $message;\r
+        $this->code      = $code;\r
+        $this->mode      = $mode;\r
+        $this->userinfo  = $userinfo;\r
+        if (function_exists("debug_backtrace")) {\r
+            if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {\r
+                $this->backtrace = debug_backtrace();\r
+            }\r
+        }\r
+        if ($mode & PEAR_ERROR_CALLBACK) {\r
+            $this->level = E_USER_NOTICE;\r
+            $this->callback = $options;\r
+        } else {\r
+            if ($options === null) {\r
+                $options = E_USER_NOTICE;\r
+            }\r
+            $this->level = $options;\r
+            $this->callback = null;\r
+        }\r
+        if ($this->mode & PEAR_ERROR_PRINT) {\r
+            if (is_null($options) || is_int($options)) {\r
+                $format = "%s";\r
+            } else {\r
+                $format = $options;\r
+            }\r
+            printf($format, $this->getMessage());\r
+        }\r
+        if ($this->mode & PEAR_ERROR_TRIGGER) {\r
+            trigger_error($this->getMessage(), $this->level);\r
+        }\r
+        if ($this->mode & PEAR_ERROR_DIE) {\r
+            $msg = $this->getMessage();\r
+            if (is_null($options) || is_int($options)) {\r
+                $format = "%s";\r
+                if (substr($msg, -1) != "\n") {\r
+                    $msg .= "\n";\r
+                }\r
+            } else {\r
+                $format = $options;\r
+            }\r
+            die(sprintf($format, $msg));\r
+        }\r
+        if ($this->mode & PEAR_ERROR_CALLBACK) {\r
+            if (is_callable($this->callback)) {\r
+                call_user_func($this->callback, $this);\r
+            }\r
+        }\r
+        if ($this->mode & PEAR_ERROR_EXCEPTION) {\r
+            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);\r
+            eval('$e = new Exception($this->message, $this->code);throw($e);');\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getMode()\r
+\r
+    /**\r
+     * Get the error mode from an error object.\r
+     *\r
+     * @return int error mode\r
+     * @access public\r
+     */\r
+    function getMode() {\r
+        return $this->mode;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getCallback()\r
+\r
+    /**\r
+     * Get the callback function/method from an error object.\r
+     *\r
+     * @return mixed callback function or object/method array\r
+     * @access public\r
+     */\r
+    function getCallback() {\r
+        return $this->callback;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getMessage()\r
+\r
+\r
+    /**\r
+     * Get the error message from an error object.\r
+     *\r
+     * @return  string  full error message\r
+     * @access public\r
+     */\r
+    function getMessage()\r
+    {\r
+        return ($this->error_message_prefix . $this->message);\r
+    }\r
+\r
+\r
+    // }}}\r
+    // {{{ getCode()\r
+\r
+    /**\r
+     * Get error code from an error object\r
+     *\r
+     * @return int error code\r
+     * @access public\r
+     */\r
+     function getCode()\r
+     {\r
+        return $this->code;\r
+     }\r
+\r
+    // }}}\r
+    // {{{ getType()\r
+\r
+    /**\r
+     * Get the name of this error/exception.\r
+     *\r
+     * @return string error/exception name (type)\r
+     * @access public\r
+     */\r
+    function getType()\r
+    {\r
+        return get_class($this);\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getUserInfo()\r
+\r
+    /**\r
+     * Get additional user-supplied information.\r
+     *\r
+     * @return string user-supplied information\r
+     * @access public\r
+     */\r
+    function getUserInfo()\r
+    {\r
+        return $this->userinfo;\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getDebugInfo()\r
+\r
+    /**\r
+     * Get additional debug information supplied by the application.\r
+     *\r
+     * @return string debug information\r
+     * @access public\r
+     */\r
+    function getDebugInfo()\r
+    {\r
+        return $this->getUserInfo();\r
+    }\r
+\r
+    // }}}\r
+    // {{{ getBacktrace()\r
+\r
+    /**\r
+     * Get the call backtrace from where the error was generated.\r
+     * Supported with PHP 4.3.0 or newer.\r
+     *\r
+     * @param int $frame (optional) what frame to fetch\r
+     * @return array Backtrace, or NULL if not available.\r
+     * @access public\r
+     */\r
+    function getBacktrace($frame = null)\r
+    {\r
+        if (defined('PEAR_IGNORE_BACKTRACE')) {\r
+            return null;\r
+        }\r
+        if ($frame === null) {\r
+            return $this->backtrace;\r
+        }\r
+        return $this->backtrace[$frame];\r
+    }\r
+\r
+    // }}}\r
+    // {{{ addUserInfo()\r
+\r
+    function addUserInfo($info)\r
+    {\r
+        if (empty($this->userinfo)) {\r
+            $this->userinfo = $info;\r
+        } else {\r
+            $this->userinfo .= " ** $info";\r
+        }\r
+    }\r
+\r
+    // }}}\r
+    // {{{ toString()\r
+\r
+    /**\r
+     * Make a string representation of this object.\r
+     *\r
+     * @return string a string with an object summary\r
+     * @access public\r
+     */\r
+    function toString() {\r
+        $modes = array();\r
+        $levels = array(E_USER_NOTICE  => 'notice',\r
+                        E_USER_WARNING => 'warning',\r
+                        E_USER_ERROR   => 'error');\r
+        if ($this->mode & PEAR_ERROR_CALLBACK) {\r
+            if (is_array($this->callback)) {\r
+                $callback = (is_object($this->callback[0]) ?\r
+                    strtolower(get_class($this->callback[0])) :\r
+                    $this->callback[0]) . '::' .\r
+                    $this->callback[1];\r
+            } else {\r
+                $callback = $this->callback;\r
+            }\r
+            return sprintf('[%s: message="%s" code=%d mode=callback '.\r
+                           'callback=%s prefix="%s" info="%s"]',\r
+                           strtolower(get_class($this)), $this->message, $this->code,\r
+                           $callback, $this->error_message_prefix,\r
+                           $this->userinfo);\r
+        }\r
+        if ($this->mode & PEAR_ERROR_PRINT) {\r
+            $modes[] = 'print';\r
+        }\r
+        if ($this->mode & PEAR_ERROR_TRIGGER) {\r
+            $modes[] = 'trigger';\r
+        }\r
+        if ($this->mode & PEAR_ERROR_DIE) {\r
+            $modes[] = 'die';\r
+        }\r
+        if ($this->mode & PEAR_ERROR_RETURN) {\r
+            $modes[] = 'return';\r
+        }\r
+        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.\r
+                       'prefix="%s" info="%s"]',\r
+                       strtolower(get_class($this)), $this->message, $this->code,\r
+                       implode("|", $modes), $levels[$this->level],\r
+                       $this->error_message_prefix,\r
+                       $this->userinfo);\r
+    }\r
+\r
+    // }}}\r
+}\r
+\r
+/*\r
+ * Local Variables:\r
+ * mode: php\r
+ * tab-width: 4\r
+ * c-basic-offset: 4\r
+ * End:\r
+ */\r
+?>\r
index 230596c..c251c3c 100644 (file)
-<?php
-// vim: tabstop=2:shiftwidth=2
-
-/**
-  * Feedback.php ($Revision: 1.1 $)
-  * 
-  * by hsur ( http://blog.cles.jp/np_cles )
-  * $Id: Feedback.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-*/
-
-/*
-  * Copyright (C) 2006 CLES. All rights reserved.
-  *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * as published by the Free Software Foundation; either version 2
-  * of the License, or (at your option) any later version.
-  * 
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  * 
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-  * 
-  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
-  * permission to link the code of this program with those files in the PEAR
-  * library that are licensed under the PHP License (or with modified versions
-  * of those files that use the same license as those files), and distribute
-  * linked combinations including the two. You must obey the GNU General Public
-  * License in all respects for all of the code used other than those files in
-  * the PEAR library that are licensed under the PHP License. If you modify
-  * this file, you may extend this exception to your version of the file,
-  * but you are not obligated to do so. If you do not wish to do so, delete
-  * this exception statement from your version.
-*/
-
-class cles_Feedback {
-       var $oPluginAdmin;
-       function CLES_Feedback(&$pluginAdmin){
-               $this->oPluginAdmin = $pluginAdmin;
-       }
-       
-       function getMenuStr(){
-               return mb_convert_encoding('動作確認/不具合報告', _CHARSET, 'UTF-8');
-       }
-       
-       function printForm($extra = '') {
-               ob_start();
-               
-               global $nucleus, $CONF;
-               
-               echo "<h2>動作確認/不具合報告</h2>";
-               echo '<p>下記より、作者への動作確認/不具合の報告を行うことができます。</p>';
-
-               // js
-               echo '<script langage="JavaScript">
-                       function selectall(){
-                               var elements = document.getElementsByTagName(\'input\');
-                               for( var i=0; i < elements.length; i++){
-                                       var e = elements[i];
-                                       if( e.type == \'checkbox\' ){
-                                               e.checked = true;
-                                       }
-                               }
-                               return false;
-                       }
-               </script>';
-               
-               echo "<h3>収集する情報と公開について</h3>";
-               echo '<p>デフォルトで必要最低限の環境情報(赤字のもの)を開発者のサーバへ送信します。<br />
-                                                       <span style="font-weight:bold; color:red">差し支えない範囲で環境情報の提供にご協力ください。</span></p>
-                                                       <p>※ 収集した情報は統計処理、及びプラグインのBugFixのみに利用されます。また統計処理した結果については公表することがあります。</p>';
-               echo '<p><a href="#" onclick="javascript:selectall();return false;">全て送信する場合はここをクリック</a></p>';
-               
-               echo "<h3>サイト固有コードについて</h3>";
-               echo '<p>動作報告の重複を取り除くため、管理画面のURLのmd5を計算したものを送信しています。この情報から管理画面のURLを復元することはできないようになっています。<a href="http://computers.yahoo.co.jp/dict/security/hash/677.html" target="_blank">md5の解説についてはこちらをご覧ください。(Yahoo!コンピュータ用語辞典)</a></p>';
-               
-               // form 
-               echo '<form method="post" action="http://blog.cles.jp/support/report.php">' . "\n";
-
-               // table
-               echo "<table>\n";
-               echo "<tr>\n";
-               echo "<th>項目の説明</th>\n";
-               echo "<th>送信される値</th>\n";
-               echo "<th><a href=\"#\" onclick=\"javascript:selectall();return false;\">全て送信する</th>\n";
-               echo "</tr>\n";
-
-               $res = sql_query("show variables like 'version'");
-               $assoc = mysql_fetch_assoc($res);
-               $mysqlVersion = $assoc['Value'];
-               
-               if( function_exists('gd_info') )
-                       $gdinfo = @gd_info();
-               else
-                       $gdinfo['GD Version'] = 'GD is not supported';
-
-               global $CONF;
-
-               $this->_printtr('siteid', 'サイトの固有コード', md5(trim($CONF['AdminURL'])));
-               $this->_printtr('plugin_name', 'プラグイン名', $this->oPluginAdmin->plugin->getName());
-               $this->_printtr('plugin_version', 'プラグインのバージョン', $this->oPluginAdmin->plugin->getVersion());
-               $this->_printtr('plugin_info', 'プラグインの情報', $extra, true);
-               $this->_printtr('nucleus_version', 'Nucleusのバージョン', $nucleus['version'], true);
-               $this->_printtr('nucleus_charset', 'Nucleusのキャラクタセット', _CHARSET);
-               $this->_printtr('php_version', 'PHPのバージョン', PHP_VERSION, true);
-               $this->_printtr('php_sapi', 'PHPの種類', php_sapi_name());
-               $this->_printtr('php_os', 'OSの種類', PHP_OS, true);
-               $this->_printtr('php_safemode', 'セーフモードの有無', ini_get('safe_mode') ? 'on' : 'off');
-               $this->_printtr('php_gd_version', 'GDのバージョン', $gdinfo['GD Version'], true);
-               $this->_printtr('php_gd_support', 'サポートしているイメージタイプ', implode(',', $this->_supportedImageTypes()) );
-               $this->_printtr('mysql_version', 'MySQLのバージョン', $mysqlVersion, true);
-
-               echo "<tr>\n";
-               echo "<td>このプラグインは機能しましたか?</td>\n";
-               echo '<td colsan="2"><input type="radio" name="user_intention" value="ok" />はい <br/> <input type="radio" name="intention" value="ng" />いいえ'."</td>\n";
-               echo "</tr>\n";
-
-               echo "<tr>\n";
-               echo "<td>不具合の内容をお寄せください<br /><em>必ず回答が必要な質問については、<a href=\"http://japan.nucleuscms.org/bb/\">Nucleusサポートフォーラム</a>もしくは<a href=\"http://blog.cles.jp/np_cles/\">作者ページ</a>でご質問ください。</em></td>\n";
-               echo '<td colspan="2"><textarea name="user_freetext" rows="10" cols="70"></textarea>'."</td>\n";
-               echo "</tr>\n";
-
-               echo "<tr>\n";
-               echo "<td>よろしければサイトのURLを教えてください</td>\n";
-               echo '<td colspan="2"><textarea name="user_url" rows="1" cols="70"></textarea>'."</td>\n";
-               echo "</tr>\n";
-               
-               echo "<tr>\n";
-               echo "<td>リンク集作成の際、リンクをはらせていただけますか?</td>\n";
-               echo '<td colspan="2"><input type="radio" name="user_disclose" value="yes" />はい <br/> <input type="radio" name="intention" value="no" />いいえ'."</td>\n";
-               echo "</tr>\n";
-
-               echo '<tr><td colspan="3"><div align="right"><input type="submit" name="submit" value="動作確認を送信する" /></div></td></tr>';
-               echo "</table>\n";
-               echo "</form>\n";
-               
-               $contents = ob_get_contents();
-               ob_end_clean();
-               echo mb_convert_encoding($contents, _CHARSET, 'UTF-8');
-       }
-       
-       function _printtr($name, $desc, $value, $canDisable = false) {
-               echo "<tr>\n";
-       
-               if ($canDisable) {
-                       echo "<td>".$desc."</td>\n";
-                       echo "<td>".htmlspecialchars($value)."</td>\n";
-                       echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" /></td>'."\n";
-               } else {
-                       echo '<td><span style="font-weight:bold; color:red">'.$desc."</span></td>\n";
-                       echo '<td><span style="font-weight:bold; color:red">'.htmlspecialchars($value)."</span></td>\n";
-                       echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" readonly="readonly" checked="checked"/></span></td>'."\n";
-               }
-               echo "</tr>\n";
-       }
-       
-       function _supportedImageTypes() {
-               if( !function_exists('gd_info') ) return "";
-               
-               $aSupportedTypes = array ();
-               $aPossibleImageTypeBits = array (IMG_GIF => 'GIF', IMG_JPG => 'JPG', IMG_PNG => 'PNG', IMG_WBMP => 'WBMP');
-       
-               foreach ($aPossibleImageTypeBits as $iImageTypeBits => $sImageTypeString) {
-                       if (imagetypes() & $iImageTypeBits) {
-                               $aSupportedTypes[] = $sImageTypeString;
-                       }
-               }
-       
-               return $aSupportedTypes;
-       }
-
-}
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+/**\r
+  * Feedback.php ($Revision: 1.30 $)\r
+  * \r
+  * by hsur ( http://blog.cles.jp/np_cles )\r
+  * $Id: Feedback.php,v 1.30 2008/05/18 07:01:25 hsur Exp $\r
+*/\r
+\r
+/*\r
+  * Copyright (C) 2006 CLES. All rights reserved.\r
+  *\r
+  * This program is free software; you can redistribute it and/or\r
+  * modify it under the terms of the GNU General Public License\r
+  * as published by the Free Software Foundation; either version 2\r
+  * of the License, or (at your option) any later version.\r
+  * \r
+  * This program is distributed in the hope that it will be useful,\r
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  * GNU General Public License for more details.\r
+  * \r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program; if not, write to the Free Software\r
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA\r
+  * \r
+  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives\r
+  * permission to link the code of this program with those files in the PEAR\r
+  * library that are licensed under the PHP License (or with modified versions\r
+  * of those files that use the same license as those files), and distribute\r
+  * linked combinations including the two. You must obey the GNU General Public\r
+  * License in all respects for all of the code used other than those files in\r
+  * the PEAR library that are licensed under the PHP License. If you modify\r
+  * this file, you may extend this exception to your version of the file,\r
+  * but you are not obligated to do so. If you do not wish to do so, delete\r
+  * this exception statement from your version.\r
+*/\r
+\r
+class cles_Feedback {\r
+       var $oPluginAdmin;\r
+       function CLES_Feedback(&$pluginAdmin){\r
+               $this->oPluginAdmin = $pluginAdmin;\r
+       }\r
+       \r
+       function getMenuStr(){\r
+               return mb_convert_encoding('動作確認/不具合報告', _CHARSET, 'UTF-8');\r
+       }\r
+       \r
+       function printForm($extra = '') {\r
+               ob_start();\r
+               \r
+               global $nucleus, $CONF;\r
+               \r
+               echo "<h2>動作確認/不具合報告</h2>";\r
+               echo '<p>下記より、作者への動作確認/不具合の報告を行うことができます。</p>';\r
+\r
+               // js\r
+               echo '<script langage="JavaScript">\r
+//<![CDATA[\r
+function selectall(){\r
+       var elements = document.getElementsByTagName(\'input\');\r
+       for( var i=0; i < elements.length; i++){\r
+               var e = elements[i];\r
+               if( e.type == \'checkbox\' ){\r
+                       e.checked = true;\r
+               }\r
+       }\r
+       return false;\r
+}\r
+//]]>\r
+</script>';\r
+               \r
+               echo "<h3>収集する情報と公開について</h3>";\r
+               echo '<p>デフォルトで必要最低限の環境情報(赤字のもの)を開発者のサーバへ送信します。<br />\r
+                                                       <span style="font-weight:bold; color:red">差し支えない範囲で環境情報の提供にご協力ください。</span></p>\r
+                                                       <p>※ 収集した情報は統計処理、及びプラグインのBugFixのみに利用されます。また統計処理した結果については公表することがあります。</p>';\r
+               echo '<p><a href="#" onclick="javascript:selectall();return false;">全て送信する場合はここをクリック</a></p>';\r
+               \r
+               echo "<h3>サイト固有コードについて</h3>";\r
+               echo '<p>動作報告の重複を取り除くため、管理画面のURLのmd5を計算したものを送信しています。この情報から管理画面のURLを復元することはできないようになっています。<a href="http://e-words.jp/w/MD5.html" target="_blank">md5の解説についてはこちらをご覧ください。(e-WordsIT用語辞典)</a></p>';\r
+               \r
+               // form \r
+               echo '<form method="post" action="http://blog.cles.jp/support/report.php">' . "\n";\r
+\r
+               // table\r
+               echo "<table>\n";\r
+               echo "<tr>\n";\r
+               echo "<th>項目の説明</th>\n";\r
+               echo "<th>送信される値</th>\n";\r
+               echo "<th><a href=\"#\" onclick=\"javascript:selectall();return false;\">全てチェック</a></th>\n";\r
+               echo "</tr>\n";\r
+\r
+               $res = sql_query("show variables like 'version'");\r
+               $assoc = mysql_fetch_assoc($res);\r
+               $mysqlVersion = $assoc['Value'];\r
+               \r
+               if( function_exists('gd_info') )\r
+                       $gdinfo = @gd_info();\r
+               else\r
+                       $gdinfo['GD Version'] = 'GD is not supported';\r
+\r
+               global $CONF;\r
+\r
+               $this->_printtr('siteid', 'サイトの固有コード', md5(trim($CONF['AdminURL'])));\r
+               $this->_printtr('plugin_name', 'プラグイン名', $this->oPluginAdmin->plugin->getName());\r
+               $this->_printtr('plugin_version', 'プラグインのバージョン', $this->oPluginAdmin->plugin->getVersion());\r
+               $this->_printtr('plugin_info', '追加情報', $extra, true);\r
+               $this->_printtr('nucleus_version', 'Nucleusのバージョン', $nucleus['version'], true);\r
+               $this->_printtr('nucleus_charset', 'Nucleusのキャラクタセット', _CHARSET);\r
+               $this->_printtr('php_version', 'PHPのバージョン', PHP_VERSION, true);\r
+               $this->_printtr('php_sapi', 'PHPの種類', php_sapi_name());\r
+               $this->_printtr('php_os', 'OSの種類', PHP_OS, true);\r
+               $this->_printtr('php_safemode', 'セーフモードの有無', ini_get('safe_mode') ? 'on' : 'off');\r
+               $this->_printtr('php_gd_version', 'GDのバージョン', $gdinfo['GD Version'], true);\r
+               $this->_printtr('php_gd_support', 'サポートしているイメージタイプ', implode(',', $this->_supportedImageTypes()) );\r
+               $this->_printtr('mysql_version', 'MySQLのバージョン', $mysqlVersion, true);\r
+\r
+               echo "<tr>\n";\r
+               echo "<td colspan=\"2\">このプラグインは機能しましたか?</td>\n";\r
+               echo '<td><input type="radio" name="user_intention" value="ok" />はい <br/> <input type="radio" name="intention" value="ng" />いいえ'."</td>\n";\r
+               echo "</tr>\n";\r
+\r
+               echo "<tr>\n";\r
+               echo "<td>不具合の内容をお寄せください<br /><em>必ず回答が必要な質問については、<a href=\"http://japan.nucleuscms.org/bb/\">Nucleusサポートフォーラム</a>もしくは<a href=\"http://blog.cles.jp/np_cles/\">作者ページ</a>でご質問ください。</em></td>\n";\r
+               echo '<td colspan="2"><textarea name="user_freetext" rows="10" cols="70"></textarea>'."</td>\n";\r
+               echo "</tr>\n";\r
+\r
+               echo "<tr>\n";\r
+               echo "<td>よろしければサイトのURLを教えてください</td>\n";\r
+               echo '<td colspan="2"><textarea name="user_url" rows="1" cols="70"></textarea>'."</td>\n";\r
+               echo "</tr>\n";\r
+               \r
+               echo "<tr>\n";\r
+               echo "<td colspan=\"2\">リンク集作成の際、リンクをはらせていただけますか?</td>\n";\r
+               echo '<td><input type="radio" name="user_disclose" value="yes" />はい <br/> <input type="radio" name="intention" value="no" />いいえ'."</td>\n";\r
+               echo "</tr>\n";\r
+\r
+               echo '<tr><td colspan="3"><div align="right"><a href="#" onclick="javascript:selectall();return false;">全てチェック</a> <input type="submit" name="submit" value="動作確認を送信する" /></div></td></tr>';\r
+               echo "</table>\n";\r
+               echo "</form>\n";\r
+               \r
+               $contents = ob_get_contents();\r
+               ob_end_clean();\r
+               echo mb_convert_encoding($contents, _CHARSET, 'UTF-8');\r
+       }\r
+       \r
+       function _printtr($name, $desc, $value, $canDisable = false) {\r
+               echo "<tr>\n";\r
+       \r
+               if ($canDisable) {\r
+                       echo "<td>".$desc."</td>\n";\r
+                       echo "<td>".htmlspecialchars($value)."</td>\n";\r
+                       echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" /></td>'."\n";\r
+               } else {\r
+                       echo '<td><span style="font-weight:bold; color:red">'.$desc."</span></td>\n";\r
+                       echo '<td><span style="font-weight:bold; color:red">'.htmlspecialchars($value)."</span></td>\n";\r
+                       echo '<td>必須<input type="hidden" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" readonly="readonly" checked="checked"/></td>'."\n";\r
+               }\r
+               echo "</tr>\n";\r
+       }\r
+       \r
+       function _supportedImageTypes() {\r
+               if( !function_exists('gd_info') ) return "";\r
+               \r
+               $aSupportedTypes = array ();\r
+               $aPossibleImageTypeBits = array (IMG_GIF => 'GIF', IMG_JPG => 'JPG', IMG_PNG => 'PNG', IMG_WBMP => 'WBMP');\r
+       \r
+               foreach ($aPossibleImageTypeBits as $iImageTypeBits => $sImageTypeString) {\r
+                       if (imagetypes() & $iImageTypeBits) {\r
+                               $aSupportedTypes[] = $sImageTypeString;\r
+                       }\r
+               }\r
+       \r
+               return $aSupportedTypes;\r
+       }\r
+\r
+}\r
index 24d2e93..09f3ac3 100644 (file)
@@ -1,76 +1,87 @@
-<?php
-// vim: tabstop=2:shiftwidth=2
-
-/**
-  * Template.php ($Revision: 1.1 $)
-  * 
-  * by hsur ( http://blog.cles.jp/np_cles )
-  * $Id: Template.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-*/
-
-/*
-  * Copyright (C) 2006 CLES. All rights reserved.
-  *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * as published by the Free Software Foundation; either version 2
-  * of the License, or (at your option) any later version.
-  * 
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  * 
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-  * 
-  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
-  * permission to link the code of this program with those files in the PEAR
-  * library that are licensed under the PHP License (or with modified versions
-  * of those files that use the same license as those files), and distribute
-  * linked combinations including the two. You must obey the GNU General Public
-  * License in all respects for all of the code used other than those files in
-  * the PEAR library that are licensed under the PHP License. If you modify
-  * this file, you may extend this exception to your version of the file,
-  * but you are not obligated to do so. If you do not wish to do so, delete
-  * this exception statement from your version.
-*/
-
-class cles_Template {
-       var $defaultLang = 'japanese-utf8';
-       var $defalutPattern = '#{{(.*?)(\|)?}}#ie';
-       var $lang;
-       var $templateDir;
-
-       function cles_Template($templateDir) {
-               global $CONF;
-               $this->templateDir = $templateDir;
-               $this->lang = ereg_replace( '[\\|/]', '', getLanguageName());
-       }
-
-       function fetch($name, $dir = null, $suffix = 'html') {
-               $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->lang.( $suffix ? '.'.strtolower($suffix) : '' );
-               if ( ! file_exists($path) ){
-                       $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->defaultLang.( $suffix ? '.'.strtolower($suffix) : '' );
-                       if ( ! file_exists($path) )
-                               return '';
-               }
-               
-               $fsize = filesize($path);
-               if ($fsize <= 0) return '';
-               
-               $fd = fopen($path, 'r');
-               $contents = fread($fd, $fsize);
-               fclose($fd);
-               return $contents;
-       }
-       
-       function fill($template, $values, $default = null) {
-               if( $default )
-                       return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : $default', $template);
-               if( $default === null )
-                       return preg_replace($this->defalutPattern, '("$2") ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]', $template);
-               return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : "{{$1}}" ', $template);
-       }
-}
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+/**\r
+  * Template.php ($Revision: 1.11 $)\r
+  * \r
+  * by hsur ( http://blog.cles.jp/np_cles )\r
+  * $Id: Template.php,v 1.11 2008/08/15 21:17:02 hsur Exp $\r
+*/\r
+\r
+/*\r
+  * Copyright (C) 2006-2008 CLES. All rights reserved.\r
+  *\r
+  * This program is free software; you can redistribute it and/or\r
+  * modify it under the terms of the GNU General Public License\r
+  * as published by the Free Software Foundation; either version 2\r
+  * of the License, or (at your option) any later version.\r
+  * \r
+  * This program is distributed in the hope that it will be useful,\r
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  * GNU General Public License for more details.\r
+  * \r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program; if not, write to the Free Software\r
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA\r
+  * \r
+  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives\r
+  * permission to link the code of this program with those files in the PEAR\r
+  * library that are licensed under the PHP License (or with modified versions\r
+  * of those files that use the same license as those files), and distribute\r
+  * linked combinations including the two. You must obey the GNU General Public\r
+  * License in all respects for all of the code used other than those files in\r
+  * the PEAR library that are licensed under the PHP License. If you modify\r
+  * this file, you may extend this exception to your version of the file,\r
+  * but you are not obligated to do so. If you do not wish to do so, delete\r
+  * this exception statement from your version.\r
+*/\r
+\r
+if (!function_exists('getLanguageName')){\r
+       function getLanguageName(){\r
+               return 'japanese-utf8';\r
+       }\r
+}\r
+\r
+class cles_Template {\r
+       var $defaultLang = 'japanese-utf8';\r
+       var $defalutPattern = '#{{(.*?)(\|)?}}#ie';\r
+       var $lang;\r
+       var $templateDir;\r
+\r
+       function cles_Template($templateDir) {\r
+               global $CONF;\r
+               $this->templateDir = $templateDir;\r
+               $this->lang = ereg_replace( '[\\|/]', '', getLanguageName());\r
+       }\r
+\r
+       function fetch($name, $dir = null, $suffix = 'html') {\r
+               $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->lang.( $suffix ? '.'.strtolower($suffix) : '' );\r
+               if ( ! file_exists($path) ){\r
+                       $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->defaultLang.( $suffix ? '.'.strtolower($suffix) : '' );\r
+                       if ( ! file_exists($path) )\r
+                               return '';\r
+               }\r
+               \r
+               $fsize = filesize($path);\r
+               if ($fsize <= 0) return '';\r
+               \r
+               $fd = fopen($path, 'r');\r
+               $contents = fread($fd, $fsize);\r
+               fclose($fd);\r
+               return $contents;\r
+       }\r
+       \r
+       function fill($template, $values, $default = null) {\r
+               if( $default )\r
+                       return preg_replace($this->defalutPattern, 'isset($values[\'$1\']) ? (\'$2\' ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']) : $default', $template);\r
+               if( $default === null )\r
+                       return preg_replace($this->defalutPattern, '(\'$2\') ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']', $template);\r
+               return preg_replace($this->defalutPattern, 'isset($values[\'$1\']) ? (\'$2\' ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']) : \'{{$1}}\' ', $template);\r
+       }\r
+       \r
+       function fetchAndFill($name, $values, $dir = null, $suffix = 'html', $default = null){\r
+               $tpl = $this->fetch($name, $dir, $suffix);\r
+               return $this->fill($tpl, $values, $default);\r
+       }\r
+}\r
index 5a3b210..1823854 100644 (file)
@@ -1,51 +1,51 @@
-<?php
-// vim: tabstop=2:shiftwidth=2
-
-/**
-  * sharedlibs.php ($Revision: 1.1 $)
-  * 
-  * by hsur ( http://blog.cles.jp/np_cles )
-  * $Id: sharedlibs.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
-*/
-
-/*
-  * Copyright (C) 2006 CLES. All rights reserved.
-  *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * as published by the Free Software Foundation; either version 2
-  * of the License, or (at your option) any later version.
-  * 
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  * 
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-  * 
-  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
-  * permission to link the code of this program with those files in the PEAR
-  * library that are licensed under the PHP License (or with modified versions
-  * of those files that use the same license as those files), and distribute
-  * linked combinations including the two. You must obey the GNU General Public
-  * License in all respects for all of the code used other than those files in
-  * the PEAR library that are licensed under the PHP License. If you modify
-  * this file, you may extend this exception to your version of the file,
-  * but you are not obligated to do so. If you do not wish to do so, delete
-  * this exception statement from your version.
-*/
-
-if (!defined('NP_SHAREDLIBS_LOADED')) {
-       if (!defined('PATH_SEPARATOR')) {
-               if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-                       define('PATH_SEPARATOR', ';');
-               } else {
-                       define('PATH_SEPARATOR', ':');
-               }
-       }
-       ini_set('include_path', dirname(__FILE__).PATH_SEPARATOR.ini_get('include_path'));
-
-       define('NP_SHAREDLIBS_LOADED', true);
-}
+<?php\r
+// vim: tabstop=2:shiftwidth=2\r
+\r
+/**\r
+  * sharedlibs.php ($Revision: 1.8 $)\r
+  * \r
+  * by hsur ( http://blog.cles.jp/np_cles )\r
+  * $Id: sharedlibs.php,v 1.8 2006/12/12 16:51:05 hsur Exp $\r
+*/\r
+\r
+/*\r
+  * Copyright (C) 2006 CLES. All rights reserved.\r
+  *\r
+  * This program is free software; you can redistribute it and/or\r
+  * modify it under the terms of the GNU General Public License\r
+  * as published by the Free Software Foundation; either version 2\r
+  * of the License, or (at your option) any later version.\r
+  * \r
+  * This program is distributed in the hope that it will be useful,\r
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+  * GNU General Public License for more details.\r
+  * \r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program; if not, write to the Free Software\r
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA\r
+  * \r
+  * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives\r
+  * permission to link the code of this program with those files in the PEAR\r
+  * library that are licensed under the PHP License (or with modified versions\r
+  * of those files that use the same license as those files), and distribute\r
+  * linked combinations including the two. You must obey the GNU General Public\r
+  * License in all respects for all of the code used other than those files in\r
+  * the PEAR library that are licensed under the PHP License. If you modify\r
+  * this file, you may extend this exception to your version of the file,\r
+  * but you are not obligated to do so. If you do not wish to do so, delete\r
+  * this exception statement from your version.\r
+*/\r
+\r
+if (!defined('NP_SHAREDLIBS_LOADED')) {\r
+       if (!defined('PATH_SEPARATOR')) {\r
+               if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {\r
+                       define('PATH_SEPARATOR', ';');\r
+               } else {\r
+                       define('PATH_SEPARATOR', ':');\r
+               }\r
+       }\r
+       ini_set('include_path', dirname(__FILE__).PATH_SEPARATOR.ini_get('include_path'));\r
+\r
+       define('NP_SHAREDLIBS_LOADED', true);\r
+}\r