• R/O
  • HTTP
  • SSH
  • HTTPS

nucleus-plugins: Commit

Nucleus CMS日本語版用プラグインのうち、日本語版開発者がサポートしているもの


Commit MetaInfo

Revision1d1772b5702ab87b0615a9f3df68d04516b165ef (tree)
Time2010-06-06 21:05:39
Authorhsur <hsur@1ca2...>
Commiterhsur

Log Message

NP_Moblog v1.17

git-svn-id: https://svn.sourceforge.jp/svnroot/nucleus-jp/plugin@1063 1ca29b6e-896d-4ea0-84a5-967f57386b96

Change Summary

Incremental Difference

--- a/trunk/NP_Moblog/NP_Moblog.php
+++ b/trunk/NP_Moblog/NP_Moblog.php
@@ -1,904 +1,899 @@
1-<?php
2-// vim: tabstop=2:shiftwidth=2
3-
4-/**
5- * NP_Moblog ($Revision: 1.1 $)
6- * by hsur ( http://blog.cles.jp/np_cles )
7- * $Id: NP_Moblog.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
8- *
9- * Based on NP_HeelloWorld v0.8
10- * http://nakahara21.com/?itemid=133
11-*/
12-
13-/*
14- * Copyright (C) 2003 nakahara21 All rights reserved.
15- * Copyright (C) 2004-2007 cles All rights reserved.
16- *
17- * This program is free software; you can redistribute it and/or
18- * modify it under the terms of the GNU General Public License
19- * as published by the Free Software Foundation; either version 2
20- * of the License, or (at your option) any later version.
21- *
22- * This program is distributed in the hope that it will be useful,
23- * but WITHOUT ANY WARRANTY; without even the implied warranty of
24- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25- * GNU General Public License for more details.
26- *
27- * You should have received a copy of the GNU General Public License
28- * along with this program; if not, write to the Free Software
29- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30- *
31- * In addition, as a special exception, mamio and cles gives
32- * permission to link the code of this program with those files in the PEAR
33- * library that are licensed under the PHP License (or with modified versions
34- * of those files that use the same license as those files), and distribute
35- * linked combinations including the two. You must obey the GNU General Public
36- * License in all respects for all of the code used other than those files in
37- * the PEAR library that are licensed under the PHP License. If you modify
38- * this file, you may extend this exception to your version of the file,
39- * but you are not obligated to do so. If you do not wish to do so, delete
40- * this exception statement from your version.
41-*/
42-
43-global $DIR_LIBS;
44-require_once($DIR_LIBS . 'MEDIA.php');
45-
46-// クラスのロード
47-require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
48-require_once('Net/POP3.php');
49-require_once('Mail/mimeDecode.php');
50-require_once('Mail/RFC822.php');
51-
52-// バージョンチェック
53-$required = '4.3.0';
54-if( ! version_compare(phpversion() , $required , '>=') ){
55- ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
56-}
57-
58-if (!function_exists('sql_table')) {
59- function sql_table($name) {
60- return 'nucleus_'.$name;
61- }
62-}
63-
64-class NP_Moblog extends NucleusPlugin {
65-
66- // name of plugin
67- function getName() {
68- return 'Moblog';
69- }
70-
71- // author of plugin
72- function getAuthor() {
73- return 'hsur';
74- }
75-
76- // an URL to the plugin website
77- // can also be of the form mailto:foo@bar.com
78- function getURL() {
79- return 'http://blog.cles.jp/np_cles/category/31/subcatid/2';
80- }
81-
82- // version of the plugin
83- function getVersion() {
84- return '1.16';
85- }
86-
87- function hasAdminArea() {
88- return 1;
89- }
90-
91- function getEventList() {
92- return array('PrePluginOptionsEdit');
93- }
94-
95- function event_PrePluginOptionsEdit(&$data) {
96- switch($data['context']){
97- case 'member':
98- // idandcat
99- $m =& MEMBER :: createFromID($data['contextid']);
100- $trimChar = array(
101- '=' => '',
102- '|' => '',
103- ';' => '',
104- );
105-
106- $blogs = Array();
107- $res = mysql_query('SELECT bnumber, bname FROM '.sql_table('blog'));
108- while( $o = mysql_fetch_object($res) ){
109- if( $m->isTeamMember($o->bnumber) ){
110- $blogs[$o->bnumber] = $o->bname;
111- }
112- }
113-
114- $idandcatTypeInfo = '';
115- foreach($blogs as $blogid => $blogname){
116- $res = mysql_query('SELECT catid, cname FROM '.sql_table('category').' WHERE cblog='.$blogid);
117- if( @mysql_num_rows($res) > 0) {
118- while( $o = mysql_fetch_object($res) ){
119- if($idandcatTypeInfo)
120- $idandcatTypeInfo .= '|';
121- $o->cname = strtr($o->cname, $trimChar);
122- $blogname = strtr($blogname, $trimChar);
123- $idandcatTypeInfo .= "{$o->cname} ({$blogname})|{$blogid},{$o->catid}";
124- }
125- }
126- }
127- if( ! $idandcatTypeInfo ){
128- $idandcatTypeInfo = "!!投稿可能なblogがありません!!|0,0";
129- }
130-
131- // collection & thumb_col
132- $collections = MEDIA::getCollectionList();
133- $mid = intval($m->getID());
134- $collections[$mid] = 'デフォルト(useridディレクトリ)';
135-
136- $collectionTypeInfo = '';
137- foreach( $collections as $collection => $name ){
138- if($collectionTypeInfo) $collectionTypeInfo .= '|';
139- $name = strtr($name, $trimChar);
140- $collection = strtr($collection, $trimChar);
141- $collectionTypeInfo .= "{$name}|{$collection}";
142- }
143-
144- // set options
145- foreach($data['options'] as $oid => $option ){
146- switch($data['options'][$oid]['name']){
147- case 'idandcat':
148- $data['options'][$oid]['typeinfo'] = $idandcatTypeInfo;
149- break;
150- case 'collection':
151- case 'thumb_col':
152- $data['options'][$oid]['typeinfo'] = $collectionTypeInfo;
153- break;
154- }
155- }
156-
157- break;
158- default:
159- // nothing
160- }
161- }
162-
163- function install() {
164- $this->createMemberOption('enable', 'プラグインを有効にするか?', 'yesno', 'no');
165-
166- $this->createMemberOption('host', 'POP3 ホスト名', 'text', 'localhost');
167- $this->createMemberOption('port', 'POP3 ポート', 'text', '110', 'numerical=true');
168- $this->createMemberOption('user', 'POP3 ユーザー名', 'text', '');
169- $this->createMemberOption('pass', 'POP3 パスワード', 'password', '');
170- $this->createMemberOption('useAPOP', 'APOPを使用するか?', 'yesno', 'no');
171-
172- $this->createMemberOption('idandcat', 'Nucleusカテゴリ(Blog)', 'select', '', '');
173-
174- $this->createMemberOption('collection', '画像を保存するディレクトリ', 'select', '', '');
175- $this->createMemberOption('thumb_col', 'サムネイルをを保存するディレクトリ', 'select', '', '');
176-
177- $this->createMemberOption('imgonly', 'イメージ添付メールのみ追加?', 'yesno', 'no');
178- $this->createMemberOption('DefaultPublish', 'デフォルトで公開するか?', 'yesno', 'no');
179-
180- $this->createMemberOption('optionsKeyword', 'オプション記述開始の区切り文字', 'text', '@');
181- $this->createMemberOption('blogKeyword', 'オプションでblogidを指定する場合のキー', 'text', 'b');
182- $this->createMemberOption('categoryKeyword', 'オプションでカテゴリを指定する場合のキー', 'text', 'c');
183- $this->createMemberOption('publishKeyword', 'オプションでストレートにpublish指定する場合のキー', 'text', 's');
184-
185- $this->createMemberOption('moreDelimiter', '追記にする場合の区切り文字(利用しない場合は空欄)', 'text', '');
186-
187- $this->createMemberOption('accept', '投稿許可アドレス(複数の場合改行で区切ってください)', 'textarea', '');
188- $this->createMemberOption('acceptSubjectPrefix','投稿許可SubjectPrefix(制限無しの場合は空欄)','text','');
189-
190- $this->createMemberOption('nosubject', '件名がないときの題名', 'text', '');
191- $this->createMemberOption('no_strip_tags', 'htmlメールの場合に除去しないタグ', 'textarea', '<title><hr><h1><h2><h3><h4><h5><h6><div><p><pre><sup><ul><ol><br><dl><dt><table><caption><tr><li><dd><th><td><a><area><img><form><input><textarea><button><select><option>');
192- $this->createMemberOption('maxbyte', '最大添付量(B)', 'text', '300000', 'numerical=true');
193- $this->createMemberOption('subtype', '対応MIMEタイプ(正規表現)', 'text', 'gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf');
194- $this->createMemberOption('viri', '保存しないファイル(正規表現)', 'text', '.+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$');
195- $this->createMemberOption('imgExt', '画像ファイルの拡張子(正規表現)', 'text', '.+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$');
196-
197- $this->createMemberOption('thumb_ok', 'サムネイルを使用する?', 'yesno', 'yes');
198- $this->createMemberOption('W', 'サムネイルの大きさ(Width)', 'text', '120', 'numerical=true');
199- $this->createMemberOption('H', 'サムネイルの大きさ(Hight)', 'text', '120', 'numerical=true');
200- $this->createMemberOption('thumb_ext', 'サムネイルを作る対象画像', 'text', '.+\.jpe?g$|.+\.png$');
201- $this->createMemberOption('smallW', 'アイテム内に表示する画像の最大横幅', 'text', '120', 'numerical=true');
202-
203- $this->createMemberOption('textTpl', 'テキストテンプレート', 'textarea', '<%body%>');
204- $this->createMemberOption('withThumbTpl', 'サムネイル付きテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%thumbUrl%>|<%thumbW%>|<%thumbH%>|)%></a></div><%body%>');
205- $this->createMemberOption('withoutThumbTpl', 'サムネイルなしテンプレート', 'textarea', '<div class="leftbox"><%image(<%imageUrl%>|<%sizeW%>|<%sizeH%>|)%></div><%body%>');
206- $this->createMemberOption('reductionTpl', 'サムネイルなしテンプレート(縮小)', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%imageUrl%>|<%reductionW%>|<%reductionH%>|)%></a></div><%body%>');
207- $this->createMemberOption('dataTpl', 'データファイルテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%fileName%></a></div><%body%>');
208-
209- $this->createOption('execMode', '動作モード', 'select', '1', '振分対応モード|0|互換モード|1');
210- $this->createOption('spamCheck', 'SPAMチェックを有効にする', 'yesno', 'no');
211-
212- $this->createOption('interval', 'メール取得の間隔(秒)', 'text', '600', 'numerical=true');
213- $this->createOption('nextUpdate', '次回更新時刻(変更できません)', 'text', '-', 'access=readonly');
214- $this->createOption('lastUpdate', '最終更新時刻(UnixTimeStamp,)', 'text', '0', 'access=hidden');
215- $this->createOption('debug', 'ログを出力を行うか?', 'yesno', 'no');
216- }
217-
218- function unInstall() {}
219- function getMinNucleusVersion() { return 320; }
220- function getMinNucleusPatchLevel() { return 0; }
221-
222- // a description to be shown on the installed plugins listing
223- function getDescription() {
224- return '[$Revision: 1.1 $]<br />メールを拾ってアイテムを追加します。&lt;%Moblog%&gt;の記述のあるスキンを適用するページを開くと実行されます。<br />
225- &lt;%Moblog(link)%&gt;と記入することでメールを取得するためのリンクを表示することができます(要ログイン)<br />
226- 個人ごとに設定ができるようになりましたので「あなたの設定」か「メンバー管理」から設定を行ってください。';
227- }
228-
229- function supportsFeature($what) {
230- switch ($what) {
231- case 'SqlTablePrefix' :
232- case 'HelpPage':
233- return 1;
234- default :
235- return 0;
236- }
237- }
238-
239- function _info($msg) {
240- if ($this->getOption('debug') == 'yes') {
241- ACTIONLOG :: add(INFO, 'Moblog: '.$msg);
242- }
243- }
244-
245- function _warn($msg) {
246- ACTIONLOG :: add(WARNING, 'Moblog: '.$msg);
247- }
248-
249- function _getEnableUserId() {
250- $userOptions = $this->getAllMemberOptions('enable');
251- $userIds = Array ();
252- foreach( $userOptions as $userId => $value ){
253- if( $value == 'yes') $userIds[] = $userId;
254- }
255- return $userIds;
256- }
257-
258- function _initMediaDirByUserId($userId) {
259- global $DIR_MEDIA;
260-
261- $this->_info(__LINE__ . ": ユーザ($userId)の初期設定");
262- $this->memid = $userId;
263-
264- /*-- 受信メールサーバーの設定--*/
265- $this->host = $this->getMemberOption($userId, 'host');
266- $this->port = $this->getMemberOption($userId, 'port');
267- $this->user = $this->getMemberOption($userId, 'user');
268- $this->pass = $this->getMemberOption($userId, 'pass');
269-
270- // メールでアイテムを追加するblogのID
271- $idandcat = $this->getMemberOption($userId, 'idandcat');
272- list($this->blogid, $this->categoryNameOrId) = explode(",", $idandcat);
273-
274- $this->imgonly = ($this->getMemberOption($userId, 'imgonly') == 'yes') ? 1 : 0;
275- $this->DefaultPublish = ($this->getMemberOption($userId, 'DefaultPublish') == 'yes') ? 1 : 0;
276-
277- /*-- メールのタイトルに各種オプションを含める場合の設定--*/
278- $this->optionsKeyword = $this->getMemberOption($userId, 'optionsKeyword');
279- $this->blogKeyword = $this->getMemberOption($userId, 'blogKeyword');
280- $this->categoryKeyword = $this->getMemberOption($userId, 'categoryKeyword');
281- $this->publishKeyword = $this->getMemberOption($userId, 'publishKeyword');
282-
283- // 投稿許可アドレス
284- $this->accept = explode("\n", $this->getMemberOption($userId, 'accept'));
285- $this->accept = Array_Map("Trim", $this->accept);
286- $this->accept = Array_Map("strtolower", $this->accept);
287- foreach( $this->accept as $mailAddr ){
288- $this->_info(__LINE__ . "許可アドレス user:$userId, $mailAddr");
289- }
290-
291- // 投稿許可SubjectPrefix
292- $this->acceptSubjectPrefix = Trim($this->getMemberOption($userId, 'acceptSubjectPrefix'));
293-
294- // 追記の区切り
295- $this->moreDelimiter = Trim($this->getMemberOption($userId, 'moreDelimiter'));
296-
297- // 件名がないときの題名
298- $this->nosubject = $this->getMemberOption($userId, 'nosubject');
299- $this->no_strip_tags = $this->getMemberOption($userId, 'no_strip_tags');
300-
301- // 最大添付量(バイト・1ファイルにつき)※超えるものは保存しない
302- $this->maxbyte = $this->getMemberOption($userId, 'maxbyte');
303- $this->subtype = $this->getMemberOption($userId, 'subtype');
304- $this->viri = $this->getMemberOption($userId, 'viri');
305- $this->imgExt = $this->getMemberOption($userId, 'imgExt');
306-
307- // サムネイル
308- $this->thumb_ok = ($this->getMemberOption($userId, 'thumb_ok') == 'yes') ? 1 : 0;
309- $this->W = $this->getMemberOption($userId, 'W');
310- $this->H = $this->getMemberOption($userId, 'H');
311- $this->thumb_ext = $this->getMemberOption($userId, 'thumb_ext');
312- $this->smallW = $this->getMemberOption($userId, 'smallW');
313-
314- // 画像保存ディレクトリ
315- $collections = MEDIA::getCollectionList();
316- $collection = $this->getMemberOption($userId, 'collection');
317- if( $collection && isset($collections[$collection]) ){
318- $this->collection = $collection;
319- } else {
320- $this->_info(__LINE__ . ": 画像保存ディレクトリが正しくありません。デフォルトを使用します");
321- $this->collection = $this->memid;
322- }
323-
324- $this->tmpdir = $DIR_MEDIA.$this->collection.'/';
325- $this->_info(__LINE__ . ": 画像保存ディレクトリ: $this->tmpdir");
326-
327- if (!@is_dir($this->tmpdir)) {
328- $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないので、ディレクトリを作成します。");
329- $oldumask = umask(0000);
330- if (!@mkdir($this->tmpdir, 0777))
331- return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
332- umask($oldumask);
333- }
334-
335- if (!is_writable($this->tmpdir)) {
336- $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないか、書き込み可能になっていません");
337- }
338-
339- // サムネイル保存ディレクトリ
340- $thumb_collection = $this->getMemberOption($userId, 'thumb_col');
341- if( $collection && isset($collections[$thumb_collection]) ){
342- $this->thumb_collection = $thumb_collection;
343- } else {
344- $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリが正しくありません。デフォルトを使用します");
345- $this->thumb_collection = $this->memid;
346- }
347-
348- $this->thumb_dir = $DIR_MEDIA.$this->thumb_collection.'/';
349- $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリ: $this->thumb_dir");
350-
351- if (!@is_dir($this->thumb_dir)) {
352- $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないので、ディレクトリを作成します。");
353- $oldumask = umask(0000);
354- if (!@mkdir($this->thumb_dir, 0777))
355- return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
356- umask($oldumask);
357- }
358-
359- if (!is_writable($this->thumb_dir)) {
360- $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないか、書き込み可能になっていません");
361- }
362- }
363-
364- function _convert($str, $input_encoding = false) {
365- if( ! $input_encoding ){
366- $input_encoding = "ISO-2022-JP,ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-2022-JP";
367- $encoding = mb_detect_encoding($input_encoding);
368- if( ! $encoding )
369- $input_encoding = "ISO-2022-JP";
370- }
371- return mb_convert_encoding($str, _CHARSET, $input_encoding);
372- }
373-
374- function _addr_search($str) {
375- if( PEAR::isError($addresses = Mail_RFC822::parseAddressList($str)) ){
376- return false;
377- }
378- $addr = array_shift($addresses);
379- if($addr)
380- return $addr->mailbox . "@" .$addr->host;
381- return false;
382- }
383-
384- function _thumb_create($src, $W, $H, $thumb_dir = "./") {
385- // 画像の幅と高さとタイプを取得
386- $size = GetImageSize($src);
387- switch ($size[2]) {
388- case 1 :
389- return false;
390- break;
391- case 2 :
392- $im_in = @ImageCreateFromJPEG($src);
393- break;
394- case 3 :
395- $im_in = @ImageCreateFromPNG($src);
396- break;
397- }
398- if (!$im_in) {
399- $this->_warn(__LINE__ . ": GDをサポートしていないか、ソースが見つかりません<br>phpinfo()でGDオプションを確認してください");
400- return false;
401- }
402- // リサイズ
403- if ($size[0] > $W || $size[1] > $H) {
404- $key_w = $W / $size[0];
405- $key_h = $H / $size[1];
406- ($key_w < $key_h) ? $keys = $key_w : $keys = $key_h;
407- $out_w = $size[0] * $keys;
408- $out_h = $size[1] * $keys;
409- } else {
410- $out_w = $size[0];
411- $out_h = $size[1];
412- }
413- // 出力画像(サムネイル)のイメージを作成し、元画像をコピーします。(GD2.0用)
414- $im_out = ImageCreateTrueColor($out_w, $out_h);
415- $resize = ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $size[0], $size[1]);
416-
417- // サムネイル画像をブラウザに出力、保存
418- $filename = substr($src, strrpos($src, "/") + 1);
419- ImageJPEG($im_out, $thumb_dir.$this->_getThumbFileName($filename)); //jpgサムネイル作成
420- // 作成したイメージを破棄
421- ImageDestroy($im_in);
422- ImageDestroy($im_out);
423-
424- $this->_info(__LINE__ . ": サムネイルを作成しました" . $thumb_dir.$this->_getThumbFileName($filename));
425- return true;
426- }
427-
428- function _getThumbFileName($filename){
429- $filename = substr($filename, 0, strrpos($filename, "."));
430- return $filename."-small.jpg";
431- }
432-
433- function _getExecuteLink() {
434- global $CONF;
435- return $CONF['ActionURL'].'?action=plugin&amp;name=Moblog&amp;type=execute';
436- }
437-
438- function _checkLastupdate() {
439- $now = time();
440- $lastUpdate = $this->getOption('lastUpdate');
441- $interval = $this->getOption('interval');
442-
443- if ($lastUpdate + $interval < $now) {
444- $this->setOption('lastUpdate', $now);
445- $this->setOption('nextUpdate', date("Y-m-d H:i:s", $lastUpdate + $interval));
446- $this->_info(__LINE__ . ": 更新します。");
447- return true;
448- }
449- $this->_info(__LINE__ . ": 更新しません。次回更新は".date("Y-m-d H:i:s", $lastUpdate + $interval)."以降です。");
450- return false;
451- }
452-
453- function doSkinVar($skinType, $type = "") {
454- global $member;
455- switch ($type) {
456- case '' :
457- case 'execute' :
458- if ( $this->_checkLastupdate() )
459- $this->execute();
460- break;
461-
462- case 'link' :
463- if ( $member->isLoggedIn() )
464- echo '<a href="'.$this->_getExecuteLink().'">Add Item by Mail</a>';
465- break;
466- }
467- } //end of function doSkinVar($skinType)
468-
469- function doAction($type) {
470- global $member;
471-
472- switch ($type) {
473- case '' :
474- case 'execute' :
475- if (!$member->isLoggedIn())
476- return "ログインが必要です";
477- $this->execute();
478- header('Location: ' . serverVar('HTTP_REFERER'));
479- break;
480-
481- default :
482- return 'アクションが定義されていません: '.$type;
483- }
484- }
485-
486- function execute() {
487- $this->execMode = intval($this->getOption('execMode'));
488- if( $this->execMode == 0 ){
489- // false
490- $this->_info(__LINE__ . ": 振分対応モードで動作します。");
491- } elseif ( $this->execMode == 1 ) {
492- // true
493- $this->_info(__LINE__ . ": 互換モードで動作します");
494- }
495-
496- $enabledUserIds = $this->_getEnableUserId();
497- foreach ($enabledUserIds as $userId) {
498- $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得開始します");
499- $this->_initMediaDirByUserId($userId);
500-
501- // 接続
502- $pop3 =& new Net_POP3();
503- $pop3->_timeout = 10;
504-
505- if( ! $pop3->connect($this->host, $this->port) ){
506- $this->_warn(__LINE__ . ": POPサーバーに接続できません");
507- continue;
508- }
509-
510- // 認証
511- $authMethod = $this->getMemberOption($this->memid, 'useAPOP') == 'yes' ? 'APOP' : 'USER';
512- $this->_info(__LINE__ . ": $authMethod で認証を行います");
513- if(PEAR::isError($ret =& $pop3->login($this->user , $this->pass , $authMethod)) ){
514- $this->_warn(__LINE__ . ": 認証に失敗しました:" . $ret->getMessage() );
515- $pop3->disconnect();
516- continue;
517- }
518-
519- // 件数確認
520- $num = $pop3->numMsg();
521- $this->_info(__LINE__ . ": $num 件のメールがあります");
522- if ($num == "0") {
523- $pop3->disconnect();
524- continue;
525- }
526-
527- // Msg取得
528- for ($i = 1; $i <= $num; $i ++) {
529- if(! $msg =& $pop3->getMsg($i) ){
530- $this->_warn(__LINE__ . ": メールの取得に失敗しました。");
531- }
532-
533- $result = $this->addItemByMail($userId, $msg);
534- if( $result ){
535- $this->_info(__LINE__ . ": メッセージを削除します");
536- $pop3->deleteMsg($i);
537- }
538- }
539- //切断
540- $pop3->disconnect();
541- $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得終了しました");
542- }
543- }
544-
545- function addItemByMail($userId, $msg) {
546- // メールデコード
547- $params['include_bodies'] = TRUE;
548- $params['decode_bodies'] = TRUE;
549- $params['decode_headers'] = TRUE;
550- $params['input'] = $msg;
551- if(PEAR::isError( $decodedMsg = Mail_mimeDecode::decode($params)) ){
552- $this->_warn(__LINE__ . ": メールデコードに失敗しました:" . $decodedMsg->getMessage() );
553- return true;
554- }
555-
556- // From:
557- if ( $decodedMsg->headers['from'] ) {
558- $from = $this->_addr_search($decodedMsg->headers['from']);
559- }
560- if ( (! $from ) && $decodedMsg->headers['reply-to'] ) {
561- $from = $this->_addr_search($decodedMsg->headers['reply-to']);
562- }
563- if ( (! $from ) && $decodedMsg->headers['return-path'] ) {
564- $from = $this->_addr_search($decodedMsg->headers['return-path']);
565- }
566- if ( ! $from ){
567- $this->_warn(__LINE__ . ": メールに送信者アドレスが見つかりません");
568- return true;
569- }
570- $this->_info(__LINE__ . ": From($from)");
571-
572- // 投稿可能かチェック
573- $from = strtolower(Trim($from));
574- if ( in_array($from, $this->accept) ) {
575- $this->_info(__LINE__ . ": 投稿許可アドレスに含まれているので受付($from)");
576- } elseif( in_array( "*", $this->accept) ){
577- $this->_info(__LINE__ . ": 投稿許可アドレスにワイルドカードが含まれているので受付($from)");
578- }else {
579- if( $this->execMode == 0 ){
580- $this->_info(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)。振り分け対応モードなので他のアカウントで取得が行われる場合があります。");
581- } else {
582- $this->_warn(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)");
583- }
584- // 互換の場合は true
585- // 振り分けの場合は false
586- return $this->execMode;
587- }
588-
589- // Date:
590- $blog =& new BLOG($this->blogid);
591-
592- $timestamp = strtotime( trim($decodedMsg->headers['date']) );
593- if ($timestamp == -1){
594- $this->_info(__LINE__ . ": Dateヘッダからのtimestamp取得に失敗しました。");
595- $timestamp = 0;
596- }
597- $timestamp = $blog->getCorrectTime($timestamp);
598-
599- // Subject:
600- $subject = $this->_convert($decodedMsg->headers['subject']);
601-
602- // Subject: prefixチェック
603- if ( $this->acceptSubjectPrefix ){
604- $this->_info(__LINE__ . ": 投稿許可SubjectPrefixがあります(prefix: $this->acceptSubjectPrefix)");
605- $pos = mb_strpos($subject, $this->acceptSubjectPrefix);
606- if( $pos === 0 ){
607- // prefix切り取り
608- $subject = mb_substr($subject, mb_strlen($this->acceptSubjectPrefix) );
609- $this->_info(__LINE__ . ": 投稿許可SubjectPrefixをみつけました(prefix削除後subject: $subject)");
610- } else {
611- $this->_warn(__LINE__ . ": 投稿許可SubjectPrefixがないので拒否します($subject)");
612- return true;
613- }
614- }
615-
616- // subject: オプション分割
617- if (preg_match('/'.$this->optionsKeyword.'/i', $subject)) {
618- list ($subject, $option) = spliti($this->optionsKeyword, $subject, 2);
619-
620- $this->_info(__LINE__ . ": Subject($subject), Option($option)");
621-
622- $option = '&'.$option;
623- if (preg_match('/&' . $this->blogKeyword . '=([^&=]+)/i', $option, $word)) {
624- $this->blogid = $word[1];
625- $this->_info(__LINE__ . ': blogidを' . $this->blogid . 'で上書きします');
626- }
627- if (preg_match('/&' . $this->categoryKeyword . '=([^&=]+)/i', $option, $word)) {
628- $this->categoryNameOrId = $word[1];
629- $this->_info(__LINE__ . ': Categoryを' . $this->categoryNameOrId . 'で上書きします');
630- }
631- if (preg_match('/&' . $this->publishKeyword . '=([^&=]+)/i', $option, $word)) {
632- $this->DefaultPublish = $word[1];
633- $this->_info(__LINE__ . ($this->DefaultPublish ? ': 投稿を公開に上書きします' : ': 投稿を下書きに上書きします'));
634- }
635- }
636-
637- // Subject: 空の場合
638- if( ! $subject = trim(htmlspecialchars($subject)) ){
639- $subject = $this->nosubject;
640- }
641-
642- // body
643- $text = "";
644- if( strtolower($decodedMsg->ctype_primary) == "text" ){
645- $this->_info(__LINE__ . ": single partメッセージです");
646- $text = $this->_textPart($decodedMsg);
647- } elseif ( strtolower($decodedMsg->ctype_primary) == "multipart" ){
648- $this->_info(__LINE__ . ": multipart partメッセージです");
649- $texts = Array();
650- $fileNames = Array();
651- $this->_decodeMultiPart($decodedMsg->parts, $texts, $fileNames);
652-
653- $text = $texts['plain'];
654- if( $texts['html'] ) $text = $texts['html'];
655- }
656-
657- if ($this->imgonly && (! $fileNames) ) {
658- $this->_info(__LINE__ . ": 添付ファイルがないので書き込みません");
659- return true;
660- }
661-
662- if( $this->_isSpam($text) ){
663- $this->_warn(__LINE__ . ": SPAMのため追加しません");
664- return true;
665- }
666-
667- $body = '';
668- // body 生成
669- if ( ! $fileNames ) {
670- // 添付ファイルがない場合のbody
671- $vars = array (
672- 'body' => $text,
673- );
674- // textTpl
675- $body .= TEMPLATE :: fill($this->getMemberOption($this->memid, 'textTpl'), $vars);
676- } else {
677- // 添付ファイルがある場合のbody
678- $lastFile = array_pop($fileNames);
679-
680- foreach( $fileNames as $filename ){
681- $body .= $this->_imageHtml($filename);
682- }
683-
684- $body .= $this->_imageHtml($lastFile, $text);
685- }
686-
687- // item追加
688- $this->_info(__LINE__ . ": アイテム追加します");
689-
690- // bodyをbodyとmoreに分割
691- $more = '';
692- if( $this->moreDelimiter )
693- list($body, $more) = spliti($this->moreDelimiter, $body, 2);
694-
695- $body = trim($body);
696- $more = trim($more);
697-
698- $this->_addDatedItem($this->blogid, $subject, $body, $more, 0, $timestamp, 0, $this->categoryNameOrId);
699- return true;
700- }
701-
702- function _decodeMultiPart($parts, &$texts, &$fileNames){
703- foreach($parts as $part){ switch ( strtolower( $part->ctype_primary )){
704- // multipart
705- case 'multipart':
706- $this->_decodeMultiPart($part->parts, $texts, $fileNames);
707- break;
708- //text part
709- case 'text':
710- $this->_info(__LINE__ . ": text part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
711- $texts[$part->ctype_secondary] = $this->_textPart($part);
712- break;
713- // imagepart
714- case 'image':
715- default:
716- $this->_info(__LINE__ . ": image/data part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
717- if( $fileName = $this->_imagePart($part) )
718- $fileNames[] = $fileName;
719- break;
720- }
721- }
722- }
723-
724- function _textPart(&$part){
725- $encoding = false;
726- if( $part->ctype_parameters )
727- $encoding = $this->ctype_parameters['charset'];
728-
729- $text = $this->_convert($part->body, $encoding);
730- $text = strip_tags($text, $this->no_strip_tags);
731-
732- $blog =& new BLOG($this->blogid);
733- //blog設定で改行を<br />に置換onの場合
734- if ($blog->getSetting('bconvertbreaks')) {
735- if ( strtolower($part->ctype_secondary) == 'html' ) {
736- //改行文字を削除、<br>タグを\nへ
737- $text = str_replace("\r\n", "\r", $text);
738- $text = str_replace("\r", "\n", $text);
739- $text = str_replace("\n", "", $text);
740- $text = str_replace("<br>", "\n", $text);
741- }
742- }
743- return $text;
744- }
745-
746- function _imagePart(&$part){
747- if( !$this->prefixDate ){
748- $this->prefixDate = date('YmdHis');
749- $this->fileCount = 0;
750- } else {
751- $this->fileCount += 1;
752- }
753- $this->filePrefix = $this->prefixDate . sprintf('%02d', $this->fileCount);
754-
755- $filename = "";
756- if( $part->d_parameters ){
757- $filename = $part->d_parameters['filename'];
758- } elseif( $part->ctype_parameters ){
759- $filename = $part->ctype_parameters['name'];
760- } else {
761- $filename = $part->ctype_secondary;
762- }
763-
764- $filename = $this->_convert($filename);
765- $filename = $this->filePrefix . "-" . $filename;
766- $this->_info(__LINE__ . ": FileName($filename)");
767-
768- // subtypeチェック
769- $size = strlen($part->body);
770- if( eregi($this->subtype, trim($part->ctype_secondary) )){
771- // サイズ、拡張子チェック
772- if ($size < $this->maxbyte && !eregi($this->viri, $filename)) {
773-
774- $fp = fopen($this->tmpdir.$filename, "w");
775- fputs($fp, $part->body);
776- fclose($fp);
777-
778- $size = @getimagesize($this->tmpdir.$filename);
779- //サムネイル作成する場合
780- if ($this->thumb_ok && function_exists('ImageCreate')) {
781- //サムネイル作成する拡張子の場合
782- if ( preg_match("/$this->thumb_ext/i", $filename) ) {
783- if ($size[0] > $this->W || $size[1] > $this->H) {
784- $this->_thumb_create($this->tmpdir.$filename, $this->W, $this->H, $this->thumb_dir);
785- }
786- }
787- }
788- return $filename;
789- }
790- $this->_warn(__LINE__ . ": 添付ファイルを無視します。(サイズ超過: $size B or 保存しないファイルに該当しています) [$part->ctype_primary/$part->ctype_secondary]");
791- return false;
792- }
793- $this->_warn(__LINE__ . ": 添付ファイルを無視します。(subtypeチェック: $part->ctype_secondary が対応MIMEタイプに入っていますか?) [$part->ctype_primary/$part->ctype_secondary]");
794- return false;
795- }
796-
797- function _imageHtml($filename, $body = ""){
798- global $CONF;
799-
800- $size = @getimagesize($this->tmpdir.$filename);
801- $thumb_size = @getimagesize($this->thumb_dir.$this->_getThumbFileName($filename));
802- $smallH = round($this->smallW / $size[0] * $size[1], 0);
803-
804- $vars = array (
805- 'thumbW' => $thumb_size[0],
806- 'thumbH' => $thumb_size[1],
807- 'reductionW' => $this->smallW,
808- 'reductionH' => $smallH,
809- 'sizeW' => $size[0],
810- 'sizeH' => $size[1],
811- 'body' => $body,
812- 'thumbUrl' => $this->thumb_collection.'/'.$this->_getThumbFileName($filename),
813- 'imageUrl' => $this->collection.'/'.$filename,
814- 'mediaUrl' => $CONF['MediaURL'],
815- 'fileName' => $filename
816- );
817-
818- // データファイルチェック
819- if( ! preg_match("/$this->imgExt/i", $filename) ){
820- $this->_info(__LINE__ . ": 画像ファイルに該当しないので、データファイルテンプレートを使用します");
821- return TEMPLATE :: fill($this->getMemberOption($this->memid, 'dataTpl'), $vars);
822- }
823-
824- if ( $thumb_size[0] ) { //サムネイルがある場合のソース
825- $this->_info(__LINE__ . ": サムネイルがあります");
826- return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withThumbTpl'), $vars);
827- } else { //サムネイルがない場合のソース
828- if ($size[0] > $this->smallW) { //縮小表示
829- $this->_info(__LINE__ . ": サムネイルがありません、縮小表示します");
830- return TEMPLATE :: fill($this->getMemberOption($this->memid, 'reductionTpl'), $vars);
831- } else { //そのまま表示
832- $this->_info(__LINE__ . ": サムネイルがありません");
833- return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withoutThumbTpl'), $vars);
834- }
835- }
836- }
837-
838- function _addDatedItem($blogid, $title, $body, $more, $closed, $timestamp, $future, $catNameOrId = "") {
839- // 1. ログイン======================
840- $mem = MEMBER :: createFromID($this->memid);
841-
842- // 2. ブログ追加できるかチェック======================
843- if (!BLOG :: existsID($this->blogid)) {
844- $this->_info(__LINE__ . ": 存在しないblogです");
845- return false;
846- }
847- $this->_info(__LINE__ . ": blogidはOK!");
848-
849- if (!$mem->isTeamMember($blogid)) {
850- $this->_warn(__LINE__ . ": メンバーではありません");
851- return false;
852- }
853- $this->_info(__LINE__ . ": メンバーチェックもok!");
854-
855- if (!trim($body)) {
856- $this->_warn(__LINE__ . ": 空のアイテムは追加できません");
857- return false;
858- }
859- $this->_info(__LINE__ . ": アイテムは空じゃないです");
860-
861- // 3. 値の補完
862- $blog =& new BLOG($this->blogid);
863- if( $blog->isValidCategory($catNameOrId) ){
864- // カテゴリIDとして有効なときはそのまま使う
865- $catid = $catNameOrId;
866- } else {
867- // カテゴリID ゲット (誤ったカテゴリID使用時はデフォを使用)
868- $catid = $blog->getCategoryIdFromName($catNameOrId);
869- }
870-
871- $this->_info(__LINE__ . ": 追加するcatid: ".$catid);
872- if ($this->DefaultPublish) {
873- $draft = 0;
874- } else {
875- $draft = 1; //ドラフト追加
876- $this->_info(__LINE__ . ": ドラフトで追加します");
877- }
878- if ($closed != 1)
879- $closed = 0; //コメントを許可
880- $this->_info(__LINE__ . ": \$catid:".$catid.", \$draft:".$draft.", \$closed:".$closed);
881-
882- // 4. blogに追加
883- $itemid = $blog->additem($catid, $title, $body, $more, $blogid, $mem->getID(), $timestamp, $closed, $draft);
884-
885- $this->_info(__LINE__ . ": itemid: $itemid");
886- return $itemid;
887- }
888-
889- function _isSpam($str){
890- global $manager;
891- if( $this->getOption('spamCheck') == 'yes' ){
892- $spamcheck = array (
893- 'type' => 'Moblog',
894- 'data' => $str,
895- 'return' => true,
896- 'ipblock' => false
897- );
898- $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));
899- if (isset($spamcheck['result']) && $spamcheck['result'] == true)
900- return true;
901- }
902- return false;
903- }
904-}
1+<?php
2+// vim: tabstop=2:shiftwidth=2
3+
4+/**
5+ * NP_Moblog ($Revision: 1.136 $)
6+ * by hsur ( http://blog.cles.jp/np_cles )
7+ * $Id: NP_Moblog.php,v 1.136 2010/06/06 11:44:19 hsur Exp $
8+ *
9+ * Based on NP_HeelloWorld v0.8
10+ * http://nakahara21.com/?itemid=133
11+*/
12+
13+/*
14+ * Copyright (C) 2003 nakahara21 All rights reserved.
15+ * Copyright (C) 2004-2010 cles All rights reserved.
16+ *
17+ * This program is free software; you can redistribute it and/or
18+ * modify it under the terms of the GNU General Public License
19+ * as published by the Free Software Foundation; either version 2
20+ * of the License, or (at your option) any later version.
21+ *
22+ * This program is distributed in the hope that it will be useful,
23+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+ * GNU General Public License for more details.
26+ *
27+ * You should have received a copy of the GNU General Public License
28+ * along with this program; if not, write to the Free Software
29+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30+ *
31+ * In addition, as a special exception, mamio and cles gives
32+ * permission to link the code of this program with those files in the PEAR
33+ * library that are licensed under the PHP License (or with modified versions
34+ * of those files that use the same license as those files), and distribute
35+ * linked combinations including the two. You must obey the GNU General Public
36+ * License in all respects for all of the code used other than those files in
37+ * the PEAR library that are licensed under the PHP License. If you modify
38+ * this file, you may extend this exception to your version of the file,
39+ * but you are not obligated to do so. If you do not wish to do so, delete
40+ * this exception statement from your version.
41+*/
42+
43+if (!class_exists('NucleusPlugin')) exit;
44+global $DIR_LIBS;
45+require_once($DIR_LIBS . 'MEDIA.php');
46+
47+// クラスのロード
48+require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
49+require_once('Net/POP3.php');
50+require_once('Mail/mimeDecode.php');
51+require_once('Mail/RFC822.php');
52+
53+// バージョンチェック
54+$required = '4.3.0';
55+if( ! version_compare(phpversion() , $required , '>=') ){
56+ ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
57+}
58+
59+class NP_Moblog extends NucleusPlugin {
60+
61+ // name of plugin
62+ function getName() {
63+ return 'Moblog';
64+ }
65+
66+ // author of plugin
67+ function getAuthor() {
68+ return 'hsur';
69+ }
70+
71+ // an URL to the plugin website
72+ // can also be of the form mailto:foo@bar.com
73+ function getURL() {
74+ return 'http://blog.cles.jp/np_cles/category/31/subcatid/2';
75+ }
76+
77+ // version of the plugin
78+ function getVersion() {
79+ return '1.17.0';
80+ }
81+
82+ function hasAdminArea() {
83+ return 1;
84+ }
85+
86+ function getEventList() {
87+ return array('PrePluginOptionsEdit');
88+ }
89+
90+ function event_PrePluginOptionsEdit(&$data) {
91+ switch($data['context']){
92+ case 'member':
93+ // idandcat
94+ $m =& MEMBER :: createFromID($data['contextid']);
95+ $trimChar = array(
96+ '=' => '',
97+ '|' => '',
98+ ';' => '',
99+ );
100+
101+ $blogs = Array();
102+ $res = sql_query('SELECT bnumber, bname FROM '.sql_table('blog'));
103+ while( $o = mysql_fetch_object($res) ){
104+ if( $m->isTeamMember($o->bnumber) ){
105+ $blogs[$o->bnumber] = $o->bname;
106+ }
107+ }
108+
109+ $idandcatTypeInfo = '';
110+ foreach($blogs as $blogid => $blogname){
111+ $res = sql_query('SELECT catid, cname FROM '.sql_table('category').' WHERE cblog='.$blogid);
112+ if( @mysql_num_rows($res) > 0) {
113+ while( $o = mysql_fetch_object($res) ){
114+ if($idandcatTypeInfo)
115+ $idandcatTypeInfo .= '|';
116+ $o->cname = strtr($o->cname, $trimChar);
117+ $blogname = strtr($blogname, $trimChar);
118+ $idandcatTypeInfo .= "{$o->cname} ({$blogname})|{$blogid},{$o->catid}";
119+ }
120+ }
121+ }
122+ if( ! $idandcatTypeInfo ){
123+ $idandcatTypeInfo = "!!投稿可能なblogがありません!!|0,0";
124+ }
125+
126+ // collection & thumb_col
127+ $collections = MEDIA::getCollectionList();
128+ $mid = intval($m->getID());
129+ $collections[$mid] = 'デフォルト(useridディレクトリ)';
130+
131+ $collectionTypeInfo = '';
132+ foreach( $collections as $collection => $name ){
133+ if($collectionTypeInfo) $collectionTypeInfo .= '|';
134+ $name = strtr($name, $trimChar);
135+ $collection = strtr($collection, $trimChar);
136+ $collectionTypeInfo .= "{$name}|{$collection}";
137+ }
138+
139+ // set options
140+ foreach($data['options'] as $oid => $option ){
141+ switch($data['options'][$oid]['name']){
142+ case 'idandcat':
143+ $data['options'][$oid]['typeinfo'] = $idandcatTypeInfo;
144+ break;
145+ case 'collection':
146+ case 'thumb_col':
147+ $data['options'][$oid]['typeinfo'] = $collectionTypeInfo;
148+ break;
149+ }
150+ }
151+
152+ break;
153+ default:
154+ // nothing
155+ }
156+ }
157+
158+ function install() {
159+ $this->createMemberOption('enable', 'プラグインを有効にするか?', 'yesno', 'no');
160+
161+ $this->createMemberOption('host', 'POP3 ホスト名', 'text', 'localhost');
162+ $this->createMemberOption('port', 'POP3 ポート', 'text', '110', 'numerical=true');
163+ $this->createMemberOption('user', 'POP3 ユーザー名', 'text', '');
164+ $this->createMemberOption('pass', 'POP3 パスワード', 'password', '');
165+ $this->createMemberOption('useAPOP', 'APOPを使用するか?', 'yesno', 'no');
166+
167+ $this->createMemberOption('idandcat', 'Nucleusカテゴリ(Blog)', 'select', '', '');
168+
169+ $this->createMemberOption('collection', '画像を保存するディレクトリ', 'select', '', '');
170+ $this->createMemberOption('thumb_col', 'サムネイルをを保存するディレクトリ', 'select', '', '');
171+
172+ $this->createMemberOption('imgonly', 'イメージ添付メールのみ追加?', 'yesno', 'no');
173+ $this->createMemberOption('DefaultPublish', 'デフォルトで公開するか?', 'yesno', 'no');
174+
175+ $this->createMemberOption('optionsKeyword', 'オプション記述開始の区切り文字', 'text', '@');
176+ $this->createMemberOption('blogKeyword', 'オプションでblogidを指定する場合のキー', 'text', 'b');
177+ $this->createMemberOption('categoryKeyword', 'オプションでカテゴリを指定する場合のキー', 'text', 'c');
178+ $this->createMemberOption('publishKeyword', 'オプションでストレートにpublish指定する場合のキー', 'text', 's');
179+
180+ $this->createMemberOption('moreDelimiter', '追記にする場合の区切り文字(利用しない場合は空欄)', 'text', '');
181+
182+ $this->createMemberOption('accept', '投稿許可アドレス(複数の場合改行で区切ってください)', 'textarea', '');
183+ $this->createMemberOption('acceptSubjectPrefix','投稿許可SubjectPrefix(制限無しの場合は空欄)','text','');
184+
185+ $this->createMemberOption('nosubject', '件名がないときの題名', 'text', '');
186+ $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>');
187+ $this->createMemberOption('maxbyte', '最大添付量(B)', 'text', '300000', 'numerical=true');
188+ $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');
189+ $this->createMemberOption('viri', '保存しないファイル(正規表現)', 'text', '.+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$');
190+ $this->createMemberOption('imgExt', '画像ファイルの拡張子(正規表現)', 'text', '.+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$');
191+
192+ $this->createMemberOption('thumb_ok', 'サムネイルを使用する?', 'yesno', 'yes');
193+ $this->createMemberOption('W', 'サムネイルの大きさ(Width)', 'text', '120', 'numerical=true');
194+ $this->createMemberOption('H', 'サムネイルの大きさ(Hight)', 'text', '120', 'numerical=true');
195+ $this->createMemberOption('thumb_ext', 'サムネイルを作る対象画像', 'text', '.+\.jpe?g$|.+\.png$');
196+ $this->createMemberOption('smallW', 'アイテム内に表示する画像の最大横幅', 'text', '120', 'numerical=true');
197+
198+ $this->createMemberOption('textTpl', 'テキストテンプレート', 'textarea', '<%body%>');
199+ $this->createMemberOption('withThumbTpl', 'サムネイル付きテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%thumbUrl%>|<%thumbW%>|<%thumbH%>|)%></a></div><%body%>');
200+ $this->createMemberOption('withoutThumbTpl', 'サムネイルなしテンプレート', 'textarea', '<div class="leftbox"><%image(<%imageUrl%>|<%sizeW%>|<%sizeH%>|)%></div><%body%>');
201+ $this->createMemberOption('reductionTpl', 'サムネイルなしテンプレート(縮小)', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%image(<%imageUrl%>|<%reductionW%>|<%reductionH%>|)%></a></div><%body%>');
202+ $this->createMemberOption('dataTpl', 'データファイルテンプレート', 'textarea', '<div class="leftbox"><a href="<%mediaUrl%><%imageUrl%>" target="_blank"><%fileName%></a></div><%body%>');
203+
204+ $this->createOption('execMode', '動作モード', 'select', '1', '振分対応モード|0|互換モード|1');
205+ $this->createOption('spamCheck', 'SPAMチェックを有効にする', 'yesno', 'no');
206+
207+ $this->createOption('interval', 'メール取得の間隔(秒)', 'text', '600', 'numerical=true');
208+ $this->createOption('nextUpdate', '次回更新時刻(変更できません)', 'text', '-', 'access=readonly');
209+ $this->createOption('lastUpdate', '最終更新時刻(UnixTimeStamp,)', 'text', '0', 'access=hidden');
210+ $this->createOption('debug', 'ログを出力を行うか?', 'yesno', 'no');
211+ }
212+
213+ function unInstall() {}
214+ function getMinNucleusVersion() { return 320; }
215+ function getMinNucleusPatchLevel() { return 0; }
216+
217+ // a description to be shown on the installed plugins listing
218+ function getDescription() {
219+ return '[$Revision: 1.136 $]<br />メールを拾ってアイテムを追加します。&lt;%Moblog%&gt;の記述のあるスキンを適用するページを開くと実行されます。<br />
220+ &lt;%Moblog(link)%&gt;と記入することでメールを取得するためのリンクを表示することができます(要ログイン)<br />
221+ 個人ごとに設定ができるようになりましたので「あなたの設定」か「メンバー管理」から設定を行ってください。';
222+ }
223+
224+ function supportsFeature($what) {
225+ switch ($what) {
226+ case 'SqlTablePrefix' :
227+ case 'HelpPage':
228+ return 1;
229+ default :
230+ return 0;
231+ }
232+ }
233+
234+ function _info($msg) {
235+ if ($this->getOption('debug') == 'yes') {
236+ ACTIONLOG :: add(INFO, 'Moblog: '.$msg);
237+ }
238+ }
239+
240+ function _warn($msg) {
241+ ACTIONLOG :: add(WARNING, 'Moblog: '.$msg);
242+ }
243+
244+ function _getEnableUserId() {
245+ $userOptions = $this->getAllMemberOptions('enable');
246+ $userIds = Array ();
247+ foreach( $userOptions as $userId => $value ){
248+ if( $value == 'yes') $userIds[] = $userId;
249+ }
250+ return $userIds;
251+ }
252+
253+ function _initMediaDirByUserId($userId) {
254+ global $DIR_MEDIA;
255+
256+ $this->_info(__LINE__ . ": ユーザ($userId)の初期設定");
257+ $this->memid = $userId;
258+
259+ /*-- 受信メールサーバーの設定--*/
260+ $this->host = $this->getMemberOption($userId, 'host');
261+ $this->port = $this->getMemberOption($userId, 'port');
262+ $this->user = $this->getMemberOption($userId, 'user');
263+ $this->pass = $this->getMemberOption($userId, 'pass');
264+
265+ // メールでアイテムを追加するblogのID
266+ $idandcat = $this->getMemberOption($userId, 'idandcat');
267+ list($this->blogid, $this->categoryNameOrId) = explode(",", $idandcat);
268+
269+ $this->imgonly = ($this->getMemberOption($userId, 'imgonly') == 'yes') ? 1 : 0;
270+ $this->DefaultPublish = ($this->getMemberOption($userId, 'DefaultPublish') == 'yes') ? 1 : 0;
271+
272+ /*-- メールのタイトルに各種オプションを含める場合の設定--*/
273+ $this->optionsKeyword = $this->getMemberOption($userId, 'optionsKeyword');
274+ $this->blogKeyword = $this->getMemberOption($userId, 'blogKeyword');
275+ $this->categoryKeyword = $this->getMemberOption($userId, 'categoryKeyword');
276+ $this->publishKeyword = $this->getMemberOption($userId, 'publishKeyword');
277+
278+ // 投稿許可アドレス
279+ $this->accept = explode("\n", $this->getMemberOption($userId, 'accept'));
280+ $this->accept = Array_Map("Trim", $this->accept);
281+ $this->accept = Array_Map("strtolower", $this->accept);
282+ foreach( $this->accept as $mailAddr ){
283+ $this->_info(__LINE__ . "許可アドレス user:$userId, $mailAddr");
284+ }
285+
286+ // 投稿許可SubjectPrefix
287+ $this->acceptSubjectPrefix = Trim($this->getMemberOption($userId, 'acceptSubjectPrefix'));
288+
289+ // 追記の区切り
290+ $this->moreDelimiter = Trim($this->getMemberOption($userId, 'moreDelimiter'));
291+
292+ // 件名がないときの題名
293+ $this->nosubject = $this->getMemberOption($userId, 'nosubject');
294+ $this->no_strip_tags = $this->getMemberOption($userId, 'no_strip_tags');
295+
296+ // 最大添付量(バイト・1ファイルにつき)※超えるものは保存しない
297+ $this->maxbyte = $this->getMemberOption($userId, 'maxbyte');
298+ $this->subtype = $this->getMemberOption($userId, 'subtype');
299+ $this->viri = $this->getMemberOption($userId, 'viri');
300+ $this->imgExt = $this->getMemberOption($userId, 'imgExt');
301+
302+ // サムネイル
303+ $this->thumb_ok = ($this->getMemberOption($userId, 'thumb_ok') == 'yes') ? 1 : 0;
304+ $this->W = $this->getMemberOption($userId, 'W');
305+ $this->H = $this->getMemberOption($userId, 'H');
306+ $this->thumb_ext = $this->getMemberOption($userId, 'thumb_ext');
307+ $this->smallW = $this->getMemberOption($userId, 'smallW');
308+
309+ // 画像保存ディレクトリ
310+ $collections = MEDIA::getCollectionList();
311+ $collection = $this->getMemberOption($userId, 'collection');
312+ if( $collection && isset($collections[$collection]) ){
313+ $this->collection = $collection;
314+ } else {
315+ $this->_info(__LINE__ . ": 画像保存ディレクトリが正しくありません。デフォルトを使用します");
316+ $this->collection = $this->memid;
317+ }
318+
319+ $this->tmpdir = $DIR_MEDIA.$this->collection.'/';
320+ $this->_info(__LINE__ . ": 画像保存ディレクトリ: $this->tmpdir");
321+
322+ if (!@is_dir($this->tmpdir)) {
323+ $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないので、ディレクトリを作成します。");
324+ $oldumask = umask(0000);
325+ if (!@mkdir($this->tmpdir, 0777))
326+ return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
327+ umask($oldumask);
328+ }
329+
330+ if (!is_writable($this->tmpdir)) {
331+ $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->collection} ディレクトリが存在しないか、書き込み可能になっていません");
332+ }
333+
334+ // サムネイル保存ディレクトリ
335+ $thumb_collection = $this->getMemberOption($userId, 'thumb_col');
336+ if( $collection && isset($collections[$thumb_collection]) ){
337+ $this->thumb_collection = $thumb_collection;
338+ } else {
339+ $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリが正しくありません。デフォルトを使用します");
340+ $this->thumb_collection = $this->memid;
341+ }
342+
343+ $this->thumb_dir = $DIR_MEDIA.$this->thumb_collection.'/';
344+ $this->_info(__LINE__ . ": サムネイル画像保存ディレクトリ: $this->thumb_dir");
345+
346+ if (!@is_dir($this->thumb_dir)) {
347+ $this->_warn(__LINE__ . ": {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないので、ディレクトリを作成します。");
348+ $oldumask = umask(0000);
349+ if (!@mkdir($this->thumb_dir, 0777))
350+ return $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリの作成に失敗しました。パーミッションを確認してください。");
351+ umask($oldumask);
352+ }
353+
354+ if (!is_writable($this->thumb_dir)) {
355+ $this->_warn(__LINE__ . ": 設定エラー: {$DIR_MEDIA}.{$this->thumb_dir} ディレクトリが存在しないか、書き込み可能になっていません");
356+ }
357+ }
358+
359+ function _convert($str, $input_encoding = false) {
360+ if( ! $input_encoding ){
361+ $input_encoding = "ISO-2022-JP,ASCII,JIS,UTF-8,EUC-JP,SJIS,ISO-2022-JP";
362+ $encoding = mb_detect_encoding($str, $input_encoding);
363+ if( ! $encoding )
364+ $input_encoding = "ISO-2022-JP";
365+ }
366+ return mb_convert_encoding($str, _CHARSET, $input_encoding);
367+ }
368+
369+ function _addr_search($str) {
370+ if( PEAR::isError($addresses = Mail_RFC822::parseAddressList($str)) ){
371+ return false;
372+ }
373+ $addr = array_shift($addresses);
374+ if($addr)
375+ return $addr->mailbox . "@" .$addr->host;
376+ return false;
377+ }
378+
379+ function _thumb_create($src, $W, $H, $thumb_dir = "./") {
380+ // 画像の幅と高さとタイプを取得
381+ $size = GetImageSize($src);
382+ switch ($size[2]) {
383+ case 1 :
384+ return false;
385+ break;
386+ case 2 :
387+ $im_in = @ImageCreateFromJPEG($src);
388+ break;
389+ case 3 :
390+ $im_in = @ImageCreateFromPNG($src);
391+ break;
392+ }
393+ if (!$im_in) {
394+ $this->_warn(__LINE__ . ": GDをサポートしていないか、ソースが見つかりません<br>phpinfo()でGDオプションを確認してください");
395+ return false;
396+ }
397+ // リサイズ
398+ if ($size[0] > $W || $size[1] > $H) {
399+ $key_w = $W / $size[0];
400+ $key_h = $H / $size[1];
401+ ($key_w < $key_h) ? $keys = $key_w : $keys = $key_h;
402+ $out_w = $size[0] * $keys;
403+ $out_h = $size[1] * $keys;
404+ } else {
405+ $out_w = $size[0];
406+ $out_h = $size[1];
407+ }
408+ // 出力画像(サムネイル)のイメージを作成し、元画像をコピーします。(GD2.0用)
409+ $im_out = ImageCreateTrueColor($out_w, $out_h);
410+ $resize = ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $size[0], $size[1]);
411+
412+ // サムネイル画像をブラウザに出力、保存
413+ $filename = substr($src, strrpos($src, "/") + 1);
414+ ImageJPEG($im_out, $thumb_dir.$this->_getThumbFileName($filename)); //jpgサムネイル作成
415+ // 作成したイメージを破棄
416+ ImageDestroy($im_in);
417+ ImageDestroy($im_out);
418+
419+ $this->_info(__LINE__ . ": サムネイルを作成しました" . $thumb_dir.$this->_getThumbFileName($filename));
420+ return true;
421+ }
422+
423+ function _getThumbFileName($filename){
424+ $filename = substr($filename, 0, strrpos($filename, "."));
425+ return $filename."-small.jpg";
426+ }
427+
428+ function _getExecuteLink() {
429+ global $CONF;
430+ return $CONF['ActionURL'].'?action=plugin&amp;name=Moblog&amp;type=execute';
431+ }
432+
433+ function _checkLastupdate() {
434+ $now = time();
435+ $lastUpdate = $this->getOption('lastUpdate');
436+ $interval = $this->getOption('interval');
437+
438+ if ($lastUpdate + $interval < $now) {
439+ $this->setOption('lastUpdate', $now);
440+ $this->setOption('nextUpdate', date("Y-m-d H:i:s", $lastUpdate + $interval));
441+ $this->_info(__LINE__ . ": 更新します。");
442+ return true;
443+ }
444+ $this->_info(__LINE__ . ": 更新しません。次回更新は".date("Y-m-d H:i:s", $lastUpdate + $interval)."以降です。");
445+ return false;
446+ }
447+
448+ function doSkinVar($skinType, $type = "") {
449+ global $member;
450+ switch ($type) {
451+ case '' :
452+ case 'execute' :
453+ if ( $this->_checkLastupdate() )
454+ $this->execute();
455+ break;
456+
457+ case 'link' :
458+ if ( $member->isLoggedIn() )
459+ echo '<a href="'.$this->_getExecuteLink().'">Add Item by Mail</a>';
460+ break;
461+ }
462+ } //end of function doSkinVar($skinType)
463+
464+ function doAction($type) {
465+ global $member;
466+
467+ switch ($type) {
468+ case '' :
469+ case 'execute' :
470+ if (!$member->isLoggedIn())
471+ return "ログインが必要です";
472+ $this->execute();
473+ header('Location: ' . serverVar('HTTP_REFERER'));
474+ break;
475+
476+ default :
477+ return 'アクションが定義されていません: '.$type;
478+ }
479+ }
480+
481+ function execute() {
482+ $this->execMode = intval($this->getOption('execMode'));
483+ if( $this->execMode == 0 ){
484+ // false
485+ $this->_info(__LINE__ . ": 振分対応モードで動作します。");
486+ } elseif ( $this->execMode == 1 ) {
487+ // true
488+ $this->_info(__LINE__ . ": 互換モードで動作します");
489+ }
490+
491+ $enabledUserIds = $this->_getEnableUserId();
492+ foreach ($enabledUserIds as $userId) {
493+ $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得開始します");
494+ $this->_initMediaDirByUserId($userId);
495+
496+ // 接続
497+ $pop3 =& new Net_POP3();
498+ $pop3->_timeout = 10;
499+
500+ if( ! $pop3->connect($this->host, $this->port) ){
501+ $this->_warn(__LINE__ . ": POPサーバーに接続できません");
502+ continue;
503+ }
504+
505+ // 認証
506+ $authMethod = $this->getMemberOption($this->memid, 'useAPOP') == 'yes' ? 'APOP' : 'USER';
507+ $this->_info(__LINE__ . ": $authMethod で認証を行います");
508+ if(PEAR::isError($ret =& $pop3->login($this->user , $this->pass , $authMethod)) ){
509+ $this->_warn(__LINE__ . ": 認証に失敗しました:" . $ret->getMessage() );
510+ $pop3->disconnect();
511+ continue;
512+ }
513+
514+ // 件数確認
515+ $num = $pop3->numMsg();
516+ $this->_info(__LINE__ . ": $num 件のメールがあります");
517+ if ($num == "0") {
518+ $pop3->disconnect();
519+ continue;
520+ }
521+
522+ // Msg取得
523+ for ($i = 1; $i <= $num; $i ++) {
524+ if(! $msg =& $pop3->getMsg($i) ){
525+ $this->_warn(__LINE__ . ": メールの取得に失敗しました。");
526+ }
527+
528+ $result = $this->addItemByMail($userId, $msg);
529+ if( $result ){
530+ $this->_info(__LINE__ . ": メッセージを削除します");
531+ $pop3->deleteMsg($i);
532+ }
533+ }
534+ //切断
535+ $pop3->disconnect();
536+ $this->_info(__LINE__ . ": ユーザ($userId)のメールを取得終了しました");
537+ }
538+ }
539+
540+ function addItemByMail($userId, $msg) {
541+ // メールデコード
542+ $params['include_bodies'] = TRUE;
543+ $params['decode_bodies'] = TRUE;
544+ $params['decode_headers'] = TRUE;
545+ $params['input'] = $msg;
546+ if(PEAR::isError( $decodedMsg = Mail_mimeDecode::decode($params)) ){
547+ $this->_warn(__LINE__ . ": メールデコードに失敗しました:" . $decodedMsg->getMessage() );
548+ return true;
549+ }
550+
551+ // From:
552+ if ( $decodedMsg->headers['from'] ) {
553+ $from = $this->_addr_search($decodedMsg->headers['from']);
554+ }
555+ if ( (! $from ) && $decodedMsg->headers['reply-to'] ) {
556+ $from = $this->_addr_search($decodedMsg->headers['reply-to']);
557+ }
558+ if ( (! $from ) && $decodedMsg->headers['return-path'] ) {
559+ $from = $this->_addr_search($decodedMsg->headers['return-path']);
560+ }
561+ if ( ! $from ){
562+ $this->_warn(__LINE__ . ": メールに送信者アドレスが見つかりません");
563+ return true;
564+ }
565+ $this->_info(__LINE__ . ": From($from)");
566+
567+ // 投稿可能かチェック
568+ $from = strtolower(Trim($from));
569+ if ( in_array($from, $this->accept) ) {
570+ $this->_info(__LINE__ . ": 投稿許可アドレスに含まれているので受付($from)");
571+ } elseif( in_array( "*", $this->accept) ){
572+ $this->_info(__LINE__ . ": 投稿許可アドレスにワイルドカードが含まれているので受付($from)");
573+ }else {
574+ if( $this->execMode == 0 ){
575+ $this->_info(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)。振り分け対応モードなので他のアカウントで取得が行われる場合があります。");
576+ } else {
577+ $this->_warn(__LINE__ . ": 投稿許可アドレスに含まれていないので拒否($from)");
578+ }
579+ // 互換の場合は true
580+ // 振り分けの場合は false
581+ return $this->execMode;
582+ }
583+
584+ // Date:
585+ $blog =& new BLOG($this->blogid);
586+
587+ $timestamp = strtotime( trim($decodedMsg->headers['date']) );
588+ if ($timestamp == -1){
589+ $this->_info(__LINE__ . ": Dateヘッダからのtimestamp取得に失敗しました。");
590+ $timestamp = 0;
591+ }
592+ $timestamp = $blog->getCorrectTime($timestamp);
593+
594+ // Subject:
595+ $subject = $this->_convert($decodedMsg->headers['subject']);
596+
597+ // Subject: prefixチェック
598+ if ( $this->acceptSubjectPrefix ){
599+ $this->_info(__LINE__ . ": 投稿許可SubjectPrefixがあります(prefix: $this->acceptSubjectPrefix)");
600+ $pos = mb_strpos($subject, $this->acceptSubjectPrefix);
601+ if( $pos === 0 ){
602+ // prefix切り取り
603+ $subject = mb_substr($subject, mb_strlen($this->acceptSubjectPrefix) );
604+ $this->_info(__LINE__ . ": 投稿許可SubjectPrefixをみつけました(prefix削除後subject: $subject)");
605+ } else {
606+ $this->_warn(__LINE__ . ": 投稿許可SubjectPrefixがないので拒否します($subject)");
607+ return true;
608+ }
609+ }
610+
611+ // subject: オプション分割
612+ if (preg_match('/'.$this->optionsKeyword.'/i', $subject)) {
613+ list ($subject, $option) = spliti($this->optionsKeyword, $subject, 2);
614+
615+ $this->_info(__LINE__ . ": Subject($subject), Option($option)");
616+
617+ $option = '&'.$option;
618+ if (preg_match('/&' . $this->blogKeyword . '=([^&=]+)/i', $option, $word)) {
619+ $this->blogid = $word[1];
620+ $this->_info(__LINE__ . ': blogidを' . $this->blogid . 'で上書きします');
621+ }
622+ if (preg_match('/&' . $this->categoryKeyword . '=([^&=]+)/i', $option, $word)) {
623+ $this->categoryNameOrId = $word[1];
624+ $this->_info(__LINE__ . ': Categoryを' . $this->categoryNameOrId . 'で上書きします');
625+ }
626+ if (preg_match('/&' . $this->publishKeyword . '=([^&=]+)/i', $option, $word)) {
627+ $this->DefaultPublish = $word[1];
628+ $this->_info(__LINE__ . ($this->DefaultPublish ? ': 投稿を公開に上書きします' : ': 投稿を下書きに上書きします'));
629+ }
630+ }
631+
632+ // Subject: 空の場合
633+ if( ! $subject = trim(htmlspecialchars($subject, ENT_QUOTES)) ){
634+ $subject = $this->nosubject;
635+ }
636+
637+ // body
638+ $text = "";
639+ if( strtolower($decodedMsg->ctype_primary) == "text" ){
640+ $this->_info(__LINE__ . ": single partメッセージです");
641+ $text = $this->_textPart($decodedMsg);
642+ } elseif ( strtolower($decodedMsg->ctype_primary) == "multipart" ){
643+ $this->_info(__LINE__ . ": multipart partメッセージです");
644+ $texts = Array();
645+ $fileNames = Array();
646+ $this->_decodeMultiPart($decodedMsg->parts, $texts, $fileNames);
647+
648+ $text = $texts['plain'];
649+ if( $texts['html'] ) $text = $texts['html'];
650+ }
651+
652+ if ($this->imgonly && (! $fileNames) ) {
653+ $this->_info(__LINE__ . ": 添付ファイルがないので書き込みません");
654+ return true;
655+ }
656+
657+ if( $this->_isSpam($text) ){
658+ $this->_warn(__LINE__ . ": SPAMのため追加しません");
659+ return true;
660+ }
661+
662+ $body = '';
663+ // body 生成
664+ if ( ! $fileNames ) {
665+ // 添付ファイルがない場合のbody
666+ $vars = array (
667+ 'body' => $text,
668+ );
669+ // textTpl
670+ $body .= TEMPLATE :: fill($this->getMemberOption($this->memid, 'textTpl'), $vars);
671+ } else {
672+ // 添付ファイルがある場合のbody
673+ $lastFile = array_pop($fileNames);
674+
675+ foreach( $fileNames as $filename ){
676+ $body .= $this->_imageHtml($filename);
677+ }
678+
679+ $body .= $this->_imageHtml($lastFile, $text);
680+ }
681+
682+ // item追加
683+ $this->_info(__LINE__ . ": アイテム追加します");
684+
685+ // bodyをbodyとmoreに分割
686+ $more = '';
687+ if( $this->moreDelimiter )
688+ list($body, $more) = spliti($this->moreDelimiter, $body, 2);
689+
690+ $body = trim($body);
691+ $more = trim($more);
692+
693+ $this->_addDatedItem($this->blogid, $subject, $body, $more, 0, $timestamp, 0, $this->categoryNameOrId);
694+ return true;
695+ }
696+
697+ function _decodeMultiPart($parts, &$texts, &$fileNames){
698+ foreach($parts as $part){ switch ( strtolower( $part->ctype_primary )){
699+ // multipart
700+ case 'multipart':
701+ $this->_decodeMultiPart($part->parts, $texts, $fileNames);
702+ break;
703+ //text part
704+ case 'text':
705+ $this->_info(__LINE__ . ": text part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
706+ $texts[$part->ctype_secondary] = $this->_textPart($part);
707+ break;
708+ // imagepart
709+ case 'image':
710+ default:
711+ $this->_info(__LINE__ . ": image/data part をみつけました[{$part->ctype_primary}/{$part->ctype_secondary}]");
712+ if( $fileName = $this->_imagePart($part) )
713+ $fileNames[] = $fileName;
714+ break;
715+ }
716+ }
717+ }
718+
719+ function _textPart(&$part){
720+ $encoding = false;
721+ if( $part->ctype_parameters )
722+ $encoding = $this->ctype_parameters['charset'];
723+
724+ $text = $this->_convert($part->body, $encoding);
725+ $text = strip_tags($text, $this->no_strip_tags);
726+
727+ $blog =& new BLOG($this->blogid);
728+ //blog設定で改行を<br />に置換onの場合
729+ if ($blog->getSetting('bconvertbreaks')) {
730+ if ( strtolower($part->ctype_secondary) == 'html' ) {
731+ //改行文字を削除、<br>タグを\nへ
732+ $text = str_replace("\r\n", "\r", $text);
733+ $text = str_replace("\r", "\n", $text);
734+ $text = str_replace("\n", "", $text);
735+ $text = str_replace("<br>", "\n", $text);
736+ }
737+ }
738+ return $text;
739+ }
740+
741+ function _imagePart(&$part){
742+ if( !$this->prefixDate ){
743+ $this->prefixDate = date('YmdHis');
744+ $this->fileCount = 0;
745+ } else {
746+ $this->fileCount += 1;
747+ }
748+ $this->filePrefix = $this->prefixDate . sprintf('%02d', $this->fileCount);
749+
750+ $filename = "";
751+ if( $part->d_parameters ){
752+ $filename = $part->d_parameters['filename'];
753+ } elseif( $part->ctype_parameters ){
754+ $filename = $part->ctype_parameters['name'];
755+ } else {
756+ $filename = $part->ctype_secondary;
757+ }
758+
759+ $filename = $this->_convert($filename);
760+ $filename = $this->filePrefix . "-" . $filename;
761+ $this->_info(__LINE__ . ": FileName($filename)");
762+
763+ // subtypeチェック
764+ $size = strlen($part->body);
765+ if( preg_match("/".$this->subtype."/i", trim($part->ctype_secondary) )){
766+ // サイズ、拡張子チェック
767+ if ($size < $this->maxbyte && preg_match("/".$this->viri.'/i', $filename)) {
768+
769+ $fp = fopen($this->tmpdir.$filename, "w");
770+ fputs($fp, $part->body);
771+ fclose($fp);
772+
773+ $size = @getimagesize($this->tmpdir.$filename);
774+ //サムネイル作成する場合
775+ if ($this->thumb_ok && function_exists('ImageCreate')) {
776+ //サムネイル作成する拡張子の場合
777+ if ( preg_match("/$this->thumb_ext/i", $filename) ) {
778+ if ($size[0] > $this->W || $size[1] > $this->H) {
779+ $this->_thumb_create($this->tmpdir.$filename, $this->W, $this->H, $this->thumb_dir);
780+ }
781+ }
782+ }
783+ return $filename;
784+ }
785+ $this->_warn(__LINE__ . ": 添付ファイルを無視します。(サイズ超過: $size B or 保存しないファイルに該当しています) [$part->ctype_primary/$part->ctype_secondary]");
786+ return false;
787+ }
788+ $this->_warn(__LINE__ . ": 添付ファイルを無視します。(subtypeチェック: $part->ctype_secondary が対応MIMEタイプに入っていますか?) [$part->ctype_primary/$part->ctype_secondary]");
789+ return false;
790+ }
791+
792+ function _imageHtml($filename, $body = ""){
793+ global $CONF;
794+
795+ $size = @getimagesize($this->tmpdir.$filename);
796+ $thumb_size = @getimagesize($this->thumb_dir.$this->_getThumbFileName($filename));
797+ $smallH = round($this->smallW / $size[0] * $size[1], 0);
798+
799+ $vars = array (
800+ 'thumbW' => $thumb_size[0],
801+ 'thumbH' => $thumb_size[1],
802+ 'reductionW' => $this->smallW,
803+ 'reductionH' => $smallH,
804+ 'sizeW' => $size[0],
805+ 'sizeH' => $size[1],
806+ 'body' => $body,
807+ 'thumbUrl' => $this->thumb_collection.'/' . urlencode($this->_getThumbFileName($filename)),
808+ 'imageUrl' => $this->collection.'/' . urlencode($filename),
809+ 'mediaUrl' => $CONF['MediaURL'],
810+ 'fileName' => $filename
811+ );
812+
813+ // データファイルチェック
814+ if( ! preg_match("/$this->imgExt/i", $filename) ){
815+ $this->_info(__LINE__ . ": 画像ファイルに該当しないので、データファイルテンプレートを使用します");
816+ return TEMPLATE :: fill($this->getMemberOption($this->memid, 'dataTpl'), $vars);
817+ }
818+
819+ if ( $thumb_size[0] ) { //サムネイルがある場合のソース
820+ $this->_info(__LINE__ . ": サムネイルがあります");
821+ return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withThumbTpl'), $vars);
822+ } else { //サムネイルがない場合のソース
823+ if ($size[0] > $this->smallW) { //縮小表示
824+ $this->_info(__LINE__ . ": サムネイルがありません、縮小表示します");
825+ return TEMPLATE :: fill($this->getMemberOption($this->memid, 'reductionTpl'), $vars);
826+ } else { //そのまま表示
827+ $this->_info(__LINE__ . ": サムネイルがありません");
828+ return TEMPLATE :: fill($this->getMemberOption($this->memid, 'withoutThumbTpl'), $vars);
829+ }
830+ }
831+ }
832+
833+ function _addDatedItem($blogid, $title, $body, $more, $closed, $timestamp, $future, $catNameOrId = "") {
834+ // 1. ログイン======================
835+ $mem = MEMBER :: createFromID($this->memid);
836+
837+ // 2. ブログ追加できるかチェック======================
838+ if (!BLOG :: existsID($this->blogid)) {
839+ $this->_info(__LINE__ . ": 存在しないblogです");
840+ return false;
841+ }
842+ $this->_info(__LINE__ . ": blogidはOK!");
843+
844+ if (!$mem->isTeamMember($blogid)) {
845+ $this->_warn(__LINE__ . ": メンバーではありません");
846+ return false;
847+ }
848+ $this->_info(__LINE__ . ": メンバーチェックもok!");
849+
850+ if (!trim($body)) {
851+ $this->_warn(__LINE__ . ": 空のアイテムは追加できません");
852+ return false;
853+ }
854+ $this->_info(__LINE__ . ": アイテムは空じゃないです");
855+
856+ // 3. 値の補完
857+ $blog =& new BLOG($this->blogid);
858+ if( $blog->isValidCategory($catNameOrId) ){
859+ // カテゴリIDとして有効なときはそのまま使う
860+ $catid = $catNameOrId;
861+ } else {
862+ // カテゴリID ゲット (誤ったカテゴリID使用時はデフォを使用)
863+ $catid = $blog->getCategoryIdFromName($catNameOrId);
864+ }
865+
866+ $this->_info(__LINE__ . ": 追加するcatid: ".$catid);
867+ if ($this->DefaultPublish) {
868+ $draft = 0;
869+ } else {
870+ $draft = 1; //ドラフト追加
871+ $this->_info(__LINE__ . ": ドラフトで追加します");
872+ }
873+ if ($closed != 1)
874+ $closed = 0; //コメントを許可
875+ $this->_info(__LINE__ . ": \$catid:".$catid.", \$draft:".$draft.", \$closed:".$closed);
876+
877+ // 4. blogに追加
878+ $itemid = $blog->additem($catid, $title, $body, $more, $blogid, $mem->getID(), $timestamp, $closed, $draft);
879+
880+ $this->_info(__LINE__ . ": itemid: $itemid");
881+ return $itemid;
882+ }
883+
884+ function _isSpam($str){
885+ global $manager;
886+ if( $this->getOption('spamCheck') == 'yes' ){
887+ $spamcheck = array (
888+ 'type' => 'Moblog',
889+ 'data' => $str,
890+ 'return' => true,
891+ 'ipblock' => false
892+ );
893+ $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));
894+ if (isset($spamcheck['result']) && $spamcheck['result'] == true)
895+ return true;
896+ }
897+ return false;
898+ }
899+}
--- a/trunk/NP_Moblog/moblog/help.html
+++ b/trunk/NP_Moblog/moblog/help.html
@@ -1,525 +1,534 @@
1-<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>
2-
3-<p>メールサーバからメールを取得しエントリとして追加するプラグインです。画像ファイル付きのメールにも対応しています。</p>
4-
5-<h3>必要環境</h3>
6-<p>Nucleus: 3.2以降</p>
7-<p>PHP: 4.3.x以降</p>
8-
9-<h3>ファイル構成</h3>
10-
11-<p>インストール時の構成は以下の通りになります</p>
12-
13-<p>
14-(Nucleusのpluginフォルダ)<br/>
15-├NP_Moblog.php<br/>
16-│├moblog/<br/>
17-│├index.php<br/>
18-│└help.html<br/>
19-└sharedlibs/<br/>
20- └<em>PEAR/共通ライブラリ群</em>
21-</p>
22-
23-<h3>最低限やらなければならないこと</h3>
24-
25-<p><code>NP_Moblog</code>には開発の過程でたくさんのオプション(設定項目)が追加されてきました。</p>
26-<p>これらは全て<code>NP_Moblog</code>を快適に利用するためのものですが、<strong>はじめてお使いになる方はオプションの多さにビックリされている</strong>かもしれません。ここでは<code>NP_Moblog</code>を利用するために最低限やらなければならないことについて解説します。</p>
27-
28-<p><strong>以下の手順に入る前に、NP_Moblog専用のメールアドレスを準備しておいてください。</strong></p>
29-<ol>
30- <li>プラグインをインストールする<em>(このページが確認できれば、おそらくインストールは成功しています)</em></li>
31- <li>「プラグイン管理」からNP_Moblogの「編集」を開く</li>
32- <li>「メール取得の間隔(秒)」を0にする</li>
33- <li>「オプションの保存」をクリックして内容を保存する</li>
34- <li>「スキン編集」ページを開き、自分の使っているスキンへ&lt;%Moblog%&gt;を記入する。</li>
35- <li>「あなたの設定」ページを開く</li>
36- <li>「プラグインを有効にするか?」を<code>はい</code>に変更</li>
37- <li>「POP3 ホスト名」「POP3 ユーザー名」「POP3 パスワード」にメールサーバの情報を設定。(NP_Moblogはここで指定したメールボックスの宛てのメールを取得して追加します。</li>
38- <li>「Nucleusカテゴリ名(Blog名)」に投稿時に使われるカテゴリとblogを指定する。</li>
39- <li>「投稿許可アドレス」にメール送信元のメールアドレスを設定する。</li>
40- <li>「設定の変更」をクリックして記入内容を保存する。</li>
41- <li>メールを送信して、&lt;%Moblog%&gt;を記入したページを表示する。</li>
42-</ol>
43-
44-<p><em>上記で設定しなかったオプションには通常の使用において十分であると考えられる初期値が設定済みになっています。</em></p>
45-
46-<p>投稿されない場合には「管理操作履歴」を確認してみてください。
47-投稿はされるが表示が崩れたりする場合にはテンプレートを調整するとよいでしょう。
48-また、投稿が成功したら上記の3で設定した「メール取得の間隔(秒)」を600に戻しておきましょう。</p>
49-
50-<h3>メールタイトルにオプションを記述する方法</h3>
51-
52-<p>受信したメールは「メンバーオプション」に設定されている内容にしたがって処理されますが、メールのタイトルにオプションを記述することによってそのメールだけ特別な処理をすることができます。</p>
53-
54-<p>メールタイトルによって変更可能なオプション</p>
55-<ol>
56- <li>「Nucleus blog Id」</li>
57- <li>「Nucleus カテゴリ」</li>
58- <li>「デフォルトで公開するか?」</li>
59-</ol>
60-
61-<p>例えばデフォルトの設定でタイトルを「新規アイテムのタイトル@b=2&amp;c=books&amp;s=1」とした場合、メンバー設定にかかわらず以下のような処理が行われます。</p>
62-<ol>
63- <li>「Nucleus blog Id」は2</li>
64- <li>「Nucleus カテゴリ」はbooks</li>
65- <li>「デフォルトで公開するか?」が1 <em>(すぐに公開)</em></li>
66-</ol>
67-
68-<p>カテゴリの名前が間違っているときは、指定されたblogのデフォルトのカテゴリとなります。
69-存在しないblogが指定されたときは、追加を行いません。(ログにエラーが記録されます) </p>
70-
71-<p>オプション記述方法(デフォルト時)</p>
72-<ol>
73- <li>オプションの記述開始は半角@</li>
74- <li>値を=で結び、間は&amp;</li>
75- <li>「Nucleus blog Id」のキーは&quot;b&quot;</li>
76- <li>「Nucleus カテゴリ」のキーは&quot;c&quot;</li>
77- <li>「デフォルトで公開するか?」のキーは&quot;s&quot;</li>
78-</ol>
79-
80-<h3>スキンへの記述</h3>
81-
82-<p>インストールを行っただけではメールの取得は行われません。</p>
83-<p>スキン中に以下のタグを記述することにより、メールの取得が行われるようになります。</p>
84-
85-<table>
86-<tr>
87- <th>タグ</th>
88- <th>解説</th>
89-</tr>
90-<tr>
91- <td>&lt;%Moblog%&gt;</td>
92- <td>このページが表示されるときにメールの取得を行います。<br />
93- 前回の取得処理からプラグインオプションで設定してある「メール取得の間隔(秒)」以上の時間が経過していない場合には取得処理を行いません。
94- </td>
95-</tr>
96-<tr>
97- <td>&lt;%Moblog(link)%&gt;</td>
98- <td>メンバーが<strong>ログインした状態</strong>でこのスキンを表示すると<code>Add Item by Mail</code>というリンクが出現します。これをクリックすることにより、メールの取得処理を行うことができます。<br />
99- これは上記と違い、必ずメール取得処理を行います。
100- </td>
101-</tr>
102-</table>
103-
104-<h3>オプション</h3>
105-
106-<p><code>NP_Moblog</code>には2種類のオプションがあります</p>
107-<ol>
108- <li>「プラグイン管理」から設定できる「プラグインオプション」</li>
109- <li>「あなたの設定」や「メンバー管理」からメンバーごとに設定できる「メンバーオプション」</li>
110-</ol>
111-
112-<p>プラグインオプション(サイト全体の設定)</p>
113-<table>
114-<tr>
115- <th>オプション</th>
116- <th>解説</th>
117-</tr>
118-<tr>
119- <td>動作モード</td>
120- <td>
121- 動作モードを設定します。
122- <ul>
123- <li>
124- 「互換モード」:通常はこちらを選択してください。
125- </li>
126- <li>
127- 「振分対応モード」:<strong>ひとつのメールアカウントを複数のメンバーで共有する</strong>場合にのみこちらを選択してください。<br />
128- <br />
129- メンバーオプション内の「投稿を許可するメールアドレス」に記載されていないアドレスから投稿があった場合に<strong>そのメールを削除しないようになります</strong>。
130- したがって、誰も投稿許可にしていないメールアドレスから投稿があった場合、メールボックスにはどんどんゴミが溜まっていきます。
131- この場合、<strong>予期せずメールボックスが満杯になる</strong>可能性がありますのでご注意ください。
132- </li>
133- </ul>
134- デフォルト:「互換モード」
135- </td>
136-</tr>
137-<tr>
138- <td>SPAMチェックを有効にする</td>
139- <td>
140- <code>NP_Blacklist</code>などによりメールのチェックを行います。SPAMと判定された場合にはメールは投稿されません。<br />
141- 別途、SPAM判定用のプラグインが必要です。<em>(<code>$manager->notify('SpamCheck',....)</code>に対応したもの)</em>
142- </td>
143-</tr>
144-<tr>
145- <td>メール取得の間隔(秒)</td>
146- <td>
147- <code>NP_Moblog</code>はスキンに<code>&lt;Moblog&gt;</code>と書かれたページにアクセスがあるとメールを取得するようになっています。しかしながら<code>&lt;Moblog&gt;</code>と書いてあるスキンへのアクセスが多い場合、プラグインはアクセスのたびにメールを取得するので、メールの連続取得による負荷が大きくなる場合があります。そのような状況を回避するためメールの取得処理の間隔を設定することができます。<br />
148- ※テスト時などこの機能を無効化したい場合には0を入力します。<br />
149- デフォルト: 600 (10分)
150- </td>
151-</tr>
152-<tr>
153- <td>次回更新時刻(変更できません)</td>
154- <td>
155- この時刻以降に<code>&lt;Moblog&gt;</code>と記載されているページにアクセスするとメール取得処理を行う。<br />
156- ※表示用のため変更できません。
157- </td>
158-</tr>
159-<tr>
160- <td>ログを出力を行うか?</td>
161- <td>
162- <code>はい</code>にすることにより「管理者操作履歴」ログを記録するようになります。
163- 膨大なログが出力されるので動作確認時以外は<code>いいえ</code>にしておくことを推奨します。
164- <code>いいえ</code>の場合であっても重大なエラーについては記録されます。<br />
165- デフォルト: いいえ
166- </td>
167-</tr>
168-</table>
169-
170-<p>メンバーオプション(メンバーごとの設定)</p>
171-<table>
172-<tr>
173- <th>オプション</th>
174- <th>解説</th>
175-</tr>
176-<tr>
177- <td>プラグインを有効にするか?</td>
178- <td>
179- <code>いいえ</code>の場合、このメンバーでメールの取得を行いません。<br />
180- デフォルト: いいえ
181- </td>
182-</tr>
183-<tr>
184- <td>POP3 ホスト名</td>
185- <td>メールを取得するメールサーバ名</td>
186-</tr>
187-<tr>
188- <td>POP3 ポート</td>
189- <td>
190- メールを取得するメールサーバのポート。通常は変更しなくて大丈夫です<br />
191- デフォルト: 110
192- </td>
193-</tr>
194-<tr>
195- <td>POP3 ユーザー名</td>
196- <td>メールを取得するメールサーバのユーザ名</td>
197-</tr>
198-<tr>
199- <td>POP3 パスワード</td>
200- <td>メールを取得するメールサーバのパスワード</td>
201-</tr>
202-<tr>
203- <td>APOPを使用するか?</td>
204- <td>
205- メール取得の際にAPOPを利用するかどうかを設定します。<br />
206- デフォルト: いいえ
207- </td>
208-</tr>
209-<tr>
210- <td>画像を保存するディレクトリ</td>
211- <td>
212- 画像を保存するディレクトリを指定します。<br />
213- ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
214- </td>
215-</tr>
216-<tr>
217- <td>サムネイルを保存するディレクトリ</td>
218- <td>
219- サムネイルを保存するディレクトリを指定します。<br />
220- ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
221- </td>
222-</tr>
223-<tr>
224- <td>Nucleusカテゴリ(Blog)</td>
225- <td>
226- メールをどのカテゴリ(blog)で投稿するか設定します。
227- </td>
228-</tr>
229-<tr>
230- <td>イメージ添付メールのみ追加?</td>
231- <td>添付ファイルがある場合のみ、投稿を行います。添付ファイルがないメールは無視されます。</td>
232-</tr>
233-<tr>
234- <td>デフォルトで公開するか?</td>
235- <td>
236- <code>いいえ</code>の場合には取得したメールをドラフトとして投稿します。投稿内容をドラフトにせずに即時に公開したい場合には設定を<code>はい</code>にします。<br />
237- デフォルト: いいえ
238- </td>
239-</tr>
240-<tr>
241- <td>オプション記述開始の区切り文字</td>
242- <td>
243- メールタイトルによってオプションを上書きする場合にオプションの始まりを示す文字。<br />
244- デフォルト: @
245- </td>
246-</tr>
247-<tr>
248- <td>オプションでblogidを指定する場合のキー</td>
249- <td>
250- メールタイトルによってオプションを上書きする場合に「Nucleus blog Id」を表すキー<br />
251- デフォルト: b
252- </td>
253-</tr>
254-<tr>
255- <td>オプションでカテゴリを指定する場合のキー</td>
256- <td>
257- メールタイトルによってオプションを上書きする場合に「Nucleus カテゴリ名/カテゴリID」を表すキー<br />
258- デフォルト: c
259- </td>
260-</tr>
261-<tr>
262- <td>オプションでストレートにpublish指定する場合のキー</td>
263- <td>
264- メールタイトルによってオプションを上書きする場合に「デフォルトで公開するか?」を表すキー<br />
265- デフォルト: s
266- </td>
267-</tr>
268-<tr>
269- <td>追記にする場合の区切り文字(利用しない場合は空欄)</td>
270- <td>
271- メールの本文中に指定する文字列があった場合、にその文字列以降の部分が追記になります<br />
272- デフォルト: (空欄)
273- </td>
274-</tr>
275-<tr>
276- <td>投稿許可アドレス(複数の場合改行で区切ってください)</td>
277- <td>
278- 投稿を許可するメールの送信元を指定します。ここで指定されない送信元からのメールは追加されません。<br />
279- 推奨しませんが*だけを書いた行を追加することで、不特定のユーザから投稿することが可能になります。不用意に使うとblogがSPAMだらけになる可能性がありますので十分注意してください。<br />
280- デフォルト: (空欄)
281- </td>
282-</tr>
283-<tr>
284- <td>投稿許可SubjectPrefix(制限無しの場合は空欄)</td>
285- <td>
286- Subjectが特定の文字列から始まる場合にのみ投稿が行われます。この機能を利用しないとき空欄にしておいてください。<br />
287- デフォルト: (空欄)
288- </td>
289-</tr>
290-<tr>
291- <td>件名がないときの題名</td>
292- <td>
293- メールのタイトルがない場合にここで設定した内容をタイトルとして利用します<br />
294- デフォルト: (空欄)
295- </td>
296-</tr>
297-<tr>
298- <td>htmlメールの場合に除去しないタグ</td>
299- <td>
300- メールをHTML形式で送信した際に、除去しないタグを指定します<br />
301- デフォルト: &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;
302- </td>
303-</tr>
304-<tr>
305- <td>最大添付量(B)</td>
306- <td>
307- 添付ファイルを最大容量を指定します。これ以上の容量のファイルは追加されません。また、添付ファイルの最大容量はこれ以外の設定などによって制限を受ける場合があります<br />
308- デフォルト: 300000
309- </td>
310-</tr>
311-<tr>
312- <td>対応MIMEタイプ(正規表現)</td>
313- <td>
314- 添付ファイルのMIMEタイプがここで指定するパターンに該当する場合にのみ保存を行います。<br />
315- デフォルト: gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf
316- </td>
317-</tr>
318-<tr>
319- <td>保存しないファイル(正規表現)</td>
320- <td>
321- 「対応MIMEタイプ」に該当する場合でも、ファイルがここで指定するパターンに該当する場合には添付ファイルは処理されません。<br />
322- デフォルト: .+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$
323- </td>
324-</tr>
325-<tr>
326- <td>画像ファイルの拡張子(正規表現)</td>
327- <td>
328- 「対応MIMEタイプ」に該当し、ファイルがここで指定するパターンに該当する場合に、添付ファイルが画像ファイルと認識します。それ以外はデータファイルとみなされます。<br />
329- デフォルト: .+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$
330- </td>
331-</tr>
332-<tr>
333- <td>サムネイルを使用する?</td>
334- <td>
335- サムネイルを自動的に作成するか指定します<br />
336- デフォルト: はい
337- </td>
338-</tr>
339-<tr>
340- <td>サムネイルの大きさ(Width)</td>
341- <td>
342- 生成されるサムネイルの最大横幅を指定します<br />
343- デフォルト: 120
344- </td>
345-</tr>
346-<tr>
347- <td>サムネイルの大きさ(Hight)</td>
348- <td>
349- 生成されるサムネイルの最大縦幅を指定します<br />
350- デフォルト: 120
351- </td>
352-</tr>
353-<tr>
354- <td>サムネイルを作る対象画像</td>
355- <td>
356- サムネイル生成の対象となるファイル名のパターンを指定します<br />
357- デフォルト: .+\.jpe?g$|.+\.png$
358- </td>
359-</tr>
360-<tr>
361- <td>アイテム内に表示する画像の最大横幅</td>
362- <td>
363- サムネイルを生成しない場合に、IMGタグに挿入される横幅を指定します<br />
364- デフォルト: 120
365- </td>
366-</tr>
367-<tr>
368- <td>テキストテンプレート</td>
369- <td>
370- 添付ファイルがない場合に使われるテンプレートです<br />
371- テンプレート内で使えるタグについては別途解説してあります
372- </td>
373-</tr>
374-<tr>
375- <td>サムネイル付きテンプレート</td>
376- <td>
377- サムネイルを使うように設定してあり、なおかつサムネイルがきちんと生成できた場合に使われるテンプレートです<br />
378- テンプレート内で使えるタグについては別途解説してあります
379- </td>
380-</tr>
381-<tr>
382- <td>サムネイルなしテンプレート</td>
383- <td>
384- サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超えない場合、もしくは「対応MIMEタイプ」に含まれるが「サムネイルを作る対象画像」に該当する場合に使われるテンプレートです。<br />
385- テンプレート内で使えるタグについては別途解説してあります
386- </td>
387-</tr>
388-<tr>
389- <td>サムネイルなしテンプレート(縮小)</td>
390- <td>
391- サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超える場合に使われるテンプレートです<br />
392- テンプレート内で使えるタグについては別途解説してあります
393- </td>
394-</tr>
395-<tr>
396- <td>データファイルテンプレート</td>
397- <td>
398- 画像以外の添付ファイルがあるときに使われるテンプレートです<br />
399- テンプレート内で使えるタグについては別途解説してあります
400- </td>
401-</tr>
402-</table>
403-
404-<h3>テンプレート内で利用可能なタグ</h3>
405-
406-<p>テンプレート中には以下のタグが利用できます。</p>
407-<p>(テンプレートによっては使えないものもあります)</p>
408-
409-<table>
410-<tr>
411- <th>タグ</th>
412- <th>解説</th>
413-</tr>
414-<tr>
415- <td>&lt;%sizeW%&gt;</td>
416- <td>オリジナル画像の横幅</td>
417-</tr>
418-<tr>
419- <td>&lt;%sizeH%&gt;</td>
420- <td>オリジナル画像の縦幅</td>
421-</tr>
422-<tr>
423- <td>&lt;%thumbW%&gt;</td>
424- <td>生成されたサムネイルの横幅</td>
425-</tr>
426-<tr>
427- <td>&lt;%thumbH%&gt;</td>
428- <td>生成されたサムネイルの縦幅</td>
429-</tr>
430-<tr>
431- <td>&lt;%reductionW%&gt;</td>
432- <td>画像を縮小表示するときの横幅</td>
433-</tr>
434-<tr>
435- <td>&lt;%reductionH%&gt;</td>
436- <td>画像を縮小表示するときの縦幅</td>
437-</tr>
438-<tr>
439- <td>&lt;%thumbUrl%&gt;</td>
440- <td>生成されたサムネイルのURL</td>
441-</tr>
442-<tr>
443- <td>&lt;%imageUrl%&gt;</td>
444- <td>オリジナル画像(添付ファイル)のURL</td>
445-</tr>
446-<tr>
447- <td>&lt;%mediaUrl%&gt;</td>
448- <td>mediaディレクトリのURL</td>
449-</tr>
450-<tr>
451- <td>&lt;%fileName%&gt;</td>
452- <td>添付ファイルのファイル名</td>
453-</tr>
454-<tr>
455- <td>&lt;%body%&gt;</td>
456- <td>メールの本文</td>
457-</tr>
458-</table>
459-
460-<h3>サポートとバグレポート</h3>
461-
462-<p>問題が解決できない場合には<a href="http://japan.nucleuscms.org/bb/">Nucleus(JP)フォーラム</a>を活用しましょう。</p>
463-<p>バグレポートについては配布元の<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>にて受け付けていますので、該当のバージョンのエントリにコメント又はトラックバックでどうぞ。</p>
464-<p>「動作確認しました」というだけでも開発者には重要な情報になります。</p>
465-
466-<h3>アンインストール</h3>
467-
468-<p><code>NP_Moblog</code>を完全にアンインストールするための手順は以下の通りです</p>
469-
470-<ol>
471- <li>「プラグイン管理」からNP_Moblogをアンインストールする</li>
472- <li>Nucleusをインストールしているサーバの<code>nucleus/plugin/</code>ディレクトリから<code>NP_Moblog.php</code>と<code>moblog</code>ディレクトリを削除する</li>
473-</ol>
474-
475-<h3>バージョン履歴</h3>
476-
477-<p>新バージョンは<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>で確認してください。</p>
478-
479-<ul>
480- <li>Version 1.16: (2007/03/17)</li>
481- <li> [Fixed] メールアドレスの大文字小文字を区別しないようにした</li>
482- <li> [Fixed] 管理者が設定の変更を行った場合、useridディレクトリにきちんと画像が保存されない問題を修正</li>
483- <li>Version 1.15: (2006/09/30)</li>
484- <li> [Fixed] セキュリティの向上</li>
485- <li>Version 1.14: (2006/07/15)</li>
486- <li> [Added] 画像・サムネイルの保存先を指定できるようにした</li>
487- <li> [Fixed] ファイル名のPrefixをuniqid()からYYYYmmddHHiissnn形式(日付+連番)に変更した</li>
488- <li> [Fixed] ユーザーがSuper-Adminである場合に任意のBlog、カテゴリで投稿できる問題を修正</li>
489- <li> [Fixed] ブログ名、カテゴリ名に特定の文字が入っている場合に、投稿が出来ない問題を修正</li>
490- <li>Version 1.13: (2006/03/24)</li>
491- <li> [Fixed] HTMLメールが上手く扱えない問題を修正</li>
492- <li>Version 1.12: (2005/03/19)</li>
493- <li> [Fixed] ライセンスを変更した</li>
494- <li>Version 1.11: (2005/12/30)</li>
495- <li> [Fixed] マニュアルの誤記、XHTMLに準拠していなかった部分を修正</li>
496- <li> [Fixed] PEARファイル群のinclude()の方法を改善</li>
497- <li>Version 1.10: (2005/09/01)</li>
498- <li> [Fixed] 「イメージ添付メールのみ追加?」が「はい」の場合に、添付ファイルが存在しているにもかかわらず、エントリが追加されない不具合を修正</li>
499- <li> [Added] 管理画面から動作確認ができる機能を追加</li>
500- <li>Version 1.9: (2005/09/01)</li>
501- <li> [Changed] 「Nucleus blog Id」「Nucleus カテゴリ名/カテゴリID」を「Nucleusカテゴリ名(Blog名)」変更</li>
502- <li>Version 1.8: (2005/07/25)</li>
503- <li> [Fixed] PEARクラスのinclude()方法を変更した</li>
504- <li>Version 1.7: (2005/07/07)</li>
505- <li> [Fixed] 1.6で修正されていなかったオプション分割の不具合を修正</li>
506- <li>Version 1.6: (2005/07/02)</li>
507- <li> [Added] テキストテンプレート(添付ファイルがない場合のテンプレート)を追加</li>
508- <li> [Added] SPAM Checkに対応(別途、NP_BlackListなどが必要です)</li>
509- <li> [Added] Help(このファイル)を同梱</li>
510- <li> [Fixed] オプション分割の不具合を修正</li>
511- <li> [Fixed] Nucleus v3.2以前のバージョンにインストールできないようにした。(getMinNucleusVersionを設定)</li>
512-</ul>
513-
514-<h3>Special Thanks</h3>
515-
516-<p>NP_Moblogははまみおさんの<a href="http://nakahara21.com/?itemid=133">NP_HeelloWorld v0.8</a>を基に開発しました。原作者のまみおさんに感謝します。</p>
517-
518-<h3>開発者について</h3>
519-
520-<ul>
521- <li><a href="http://blog.cles.jp/member/1">hsur</a> (<a href="http://blog.cles.jp/">cles::blog</a>)</li>
522- <li>配布元: <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2">NP_cles() - NP_Moblog</a></li>
523- <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>
524- <li>ドネーションや仕事のご依頼も歓迎します。</li>
525-</ul>
\ No newline at end of file
1+<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>
2+
3+<p>メールサーバからメールを取得しエントリとして追加するプラグインです。画像ファイル付きのメールにも対応しています。</p>
4+
5+<h3>必要環境</h3>
6+<p>Nucleus: 3.2以降</p>
7+<p>PHP: 4.3.x以降</p>
8+
9+<h3>ファイル構成</h3>
10+
11+<p>インストール時の構成は以下の通りになります</p>
12+
13+<p>
14+(Nucleusのpluginフォルダ)<br/>
15+├NP_Moblog.php<br/>
16+│├moblog/<br/>
17+│├index.php<br/>
18+│└help.html<br/>
19+└sharedlibs/<br/>
20+ └<em>PEAR/共通ライブラリ群</em>
21+</p>
22+
23+<h3>最低限やらなければならないこと</h3>
24+
25+<p><code>NP_Moblog</code>には開発の過程でたくさんのオプション(設定項目)が追加されてきました。</p>
26+<p>これらは全て<code>NP_Moblog</code>を快適に利用するためのものですが、<strong>はじめてお使いになる方はオプションの多さにビックリされている</strong>かもしれません。ここでは<code>NP_Moblog</code>を利用するために最低限やらなければならないことについて解説します。</p>
27+
28+<p><strong>以下の手順に入る前に、NP_Moblog専用のメールアドレスを準備しておいてください。</strong></p>
29+<ol>
30+ <li>プラグインをインストールする<em>(このページが確認できれば、おそらくインストールは成功しています)</em></li>
31+ <li>「プラグイン管理」からNP_Moblogの「編集」を開く</li>
32+ <li>「メール取得の間隔(秒)」を0にする</li>
33+ <li>「オプションの保存」をクリックして内容を保存する</li>
34+ <li>「スキン編集」ページを開き、自分の使っているスキンへ&lt;%Moblog%&gt;を記入する。</li>
35+ <li>「あなたの設定」ページを開く</li>
36+ <li>「プラグインを有効にするか?」を<code>はい</code>に変更</li>
37+ <li>「POP3 ホスト名」「POP3 ユーザー名」「POP3 パスワード」にメールサーバの情報を設定。(NP_Moblogはここで指定したメールボックスの宛てのメールを取得して追加します。</li>
38+ <li>「Nucleusカテゴリ名(Blog名)」に投稿時に使われるカテゴリとblogを指定する。</li>
39+ <li>「投稿許可アドレス」にメール送信元のメールアドレスを設定する。</li>
40+ <li>「設定の変更」をクリックして記入内容を保存する。</li>
41+ <li>メールを送信して、&lt;%Moblog%&gt;を記入したページを表示する。</li>
42+</ol>
43+
44+<p><em>上記で設定しなかったオプションには通常の使用において十分であると考えられる初期値が設定済みになっています。</em></p>
45+
46+<p>投稿されない場合には「管理操作履歴」を確認してみてください。
47+投稿はされるが表示が崩れたりする場合にはテンプレートを調整するとよいでしょう。
48+また、投稿が成功したら上記の3で設定した「メール取得の間隔(秒)」を600に戻しておきましょう。</p>
49+
50+<h3>メールタイトルにオプションを記述する方法</h3>
51+
52+<p>受信したメールは「メンバーオプション」に設定されている内容にしたがって処理されますが、メールのタイトルにオプションを記述することによってそのメールだけ特別な処理をすることができます。</p>
53+
54+<p>メールタイトルによって変更可能なオプション</p>
55+<ol>
56+ <li>「Nucleus blog Id」</li>
57+ <li>「Nucleus カテゴリ」</li>
58+ <li>「デフォルトで公開するか?」</li>
59+</ol>
60+
61+<p>例えばデフォルトの設定でタイトルを「新規アイテムのタイトル@b=2&amp;c=books&amp;s=1」とした場合、メンバー設定にかかわらず以下のような処理が行われます。</p>
62+<ol>
63+ <li>「Nucleus blog Id」は2</li>
64+ <li>「Nucleus カテゴリ」はbooks</li>
65+ <li>「デフォルトで公開するか?」が1 <em>(すぐに公開)</em></li>
66+</ol>
67+
68+<p>カテゴリの名前が間違っているときは、指定されたblogのデフォルトのカテゴリとなります。
69+存在しないblogが指定されたときは、追加を行いません。(ログにエラーが記録されます) </p>
70+
71+<p>オプション記述方法(デフォルト時)</p>
72+<ol>
73+ <li>オプションの記述開始は半角@</li>
74+ <li>値を=で結び、間は&amp;</li>
75+ <li>「Nucleus blog Id」のキーは&quot;b&quot;</li>
76+ <li>「Nucleus カテゴリ」のキーは&quot;c&quot;</li>
77+ <li>「デフォルトで公開するか?」のキーは&quot;s&quot;</li>
78+</ol>
79+
80+<h3>スキンへの記述</h3>
81+
82+<p>インストールを行っただけではメールの取得は行われません。</p>
83+<p>スキン中に以下のタグを記述することにより、メールの取得が行われるようになります。</p>
84+
85+<table>
86+<tr>
87+ <th>タグ</th>
88+ <th>解説</th>
89+</tr>
90+<tr>
91+ <td>&lt;%Moblog%&gt;</td>
92+ <td>このページが表示されるときにメールの取得を行います。<br />
93+ 前回の取得処理からプラグインオプションで設定してある「メール取得の間隔(秒)」以上の時間が経過していない場合には取得処理を行いません。
94+ </td>
95+</tr>
96+<tr>
97+ <td>&lt;%Moblog(link)%&gt;</td>
98+ <td>メンバーが<strong>ログインした状態</strong>でこのスキンを表示すると<code>Add Item by Mail</code>というリンクが出現します。これをクリックすることにより、メールの取得処理を行うことができます。<br />
99+ これは上記と違い、必ずメール取得処理を行います。
100+ </td>
101+</tr>
102+</table>
103+
104+<h3>オプション</h3>
105+
106+<p><code>NP_Moblog</code>には2種類のオプションがあります</p>
107+<ol>
108+ <li>「プラグイン管理」から設定できる「プラグインオプション」</li>
109+ <li>「あなたの設定」や「メンバー管理」からメンバーごとに設定できる「メンバーオプション」</li>
110+</ol>
111+
112+<p>プラグインオプション(サイト全体の設定)</p>
113+<table>
114+<tr>
115+ <th>オプション</th>
116+ <th>解説</th>
117+</tr>
118+<tr>
119+ <td>動作モード</td>
120+ <td>
121+ 動作モードを設定します。
122+ <ul>
123+ <li>
124+ 「互換モード」:通常はこちらを選択してください。
125+ </li>
126+ <li>
127+ 「振分対応モード」:<strong>ひとつのメールアカウントを複数のメンバーで共有する</strong>場合にのみこちらを選択してください。<br />
128+ <br />
129+ メンバーオプション内の「投稿を許可するメールアドレス」に記載されていないアドレスから投稿があった場合に<strong>そのメールを削除しないようになります</strong>。
130+ したがって、誰も投稿許可にしていないメールアドレスから投稿があった場合、メールボックスにはどんどんゴミが溜まっていきます。
131+ この場合、<strong>予期せずメールボックスが満杯になる</strong>可能性がありますのでご注意ください。
132+ </li>
133+ </ul>
134+ デフォルト:「互換モード」
135+ </td>
136+</tr>
137+<tr>
138+ <td>SPAMチェックを有効にする</td>
139+ <td>
140+ <code>NP_Blacklist</code>などによりメールのチェックを行います。SPAMと判定された場合にはメールは投稿されません。<br />
141+ 別途、SPAM判定用のプラグインが必要です。<em>(<code>$manager->notify('SpamCheck',....)</code>に対応したもの)</em>
142+ </td>
143+</tr>
144+<tr>
145+ <td>メール取得の間隔(秒)</td>
146+ <td>
147+ <code>NP_Moblog</code>はスキンに<code>&lt;Moblog&gt;</code>と書かれたページにアクセスがあるとメールを取得するようになっています。しかしながら<code>&lt;Moblog&gt;</code>と書いてあるスキンへのアクセスが多い場合、プラグインはアクセスのたびにメールを取得するので、メールの連続取得による負荷が大きくなる場合があります。そのような状況を回避するためメールの取得処理の間隔を設定することができます。<br />
148+ ※テスト時などこの機能を無効化したい場合には0を入力します。<br />
149+ デフォルト: 600 (10分)
150+ </td>
151+</tr>
152+<tr>
153+ <td>次回更新時刻(変更できません)</td>
154+ <td>
155+ この時刻以降に<code>&lt;Moblog&gt;</code>と記載されているページにアクセスするとメール取得処理を行う。<br />
156+ ※表示用のため変更できません。
157+ </td>
158+</tr>
159+<tr>
160+ <td>ログを出力を行うか?</td>
161+ <td>
162+ <code>はい</code>にすることにより「管理者操作履歴」ログを記録するようになります。
163+ 膨大なログが出力されるので動作確認時以外は<code>いいえ</code>にしておくことを推奨します。
164+ <code>いいえ</code>の場合であっても重大なエラーについては記録されます。<br />
165+ デフォルト: いいえ
166+ </td>
167+</tr>
168+</table>
169+
170+<p>メンバーオプション(メンバーごとの設定)</p>
171+<table>
172+<tr>
173+ <th>オプション</th>
174+ <th>解説</th>
175+</tr>
176+<tr>
177+ <td>プラグインを有効にするか?</td>
178+ <td>
179+ <code>いいえ</code>の場合、このメンバーでメールの取得を行いません。<br />
180+ デフォルト: いいえ
181+ </td>
182+</tr>
183+<tr>
184+ <td>POP3 ホスト名</td>
185+ <td>メールを取得するメールサーバ名</td>
186+</tr>
187+<tr>
188+ <td>POP3 ポート</td>
189+ <td>
190+ メールを取得するメールサーバのポート。通常は変更しなくて大丈夫です<br />
191+ デフォルト: 110
192+ </td>
193+</tr>
194+<tr>
195+ <td>POP3 ユーザー名</td>
196+ <td>メールを取得するメールサーバのユーザ名</td>
197+</tr>
198+<tr>
199+ <td>POP3 パスワード</td>
200+ <td>メールを取得するメールサーバのパスワード</td>
201+</tr>
202+<tr>
203+ <td>APOPを使用するか?</td>
204+ <td>
205+ メール取得の際にAPOPを利用するかどうかを設定します。<br />
206+ デフォルト: いいえ
207+ </td>
208+</tr>
209+<tr>
210+ <td>画像を保存するディレクトリ</td>
211+ <td>
212+ 画像を保存するディレクトリを指定します。<br />
213+ ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
214+ </td>
215+</tr>
216+<tr>
217+ <td>サムネイルを保存するディレクトリ</td>
218+ <td>
219+ サムネイルを保存するディレクトリを指定します。<br />
220+ ※デフォルト以外を利用する場合には、あらかじめmediaディレクトリ内にディレクトリを作成しておく必要があります。
221+ </td>
222+</tr>
223+<tr>
224+ <td>Nucleusカテゴリ(Blog)</td>
225+ <td>
226+ メールをどのカテゴリ(blog)で投稿するか設定します。
227+ </td>
228+</tr>
229+<tr>
230+ <td>イメージ添付メールのみ追加?</td>
231+ <td>添付ファイルがある場合のみ、投稿を行います。添付ファイルがないメールは無視されます。</td>
232+</tr>
233+<tr>
234+ <td>デフォルトで公開するか?</td>
235+ <td>
236+ <code>いいえ</code>の場合には取得したメールをドラフトとして投稿します。投稿内容をドラフトにせずに即時に公開したい場合には設定を<code>はい</code>にします。<br />
237+ デフォルト: いいえ
238+ </td>
239+</tr>
240+<tr>
241+ <td>オプション記述開始の区切り文字</td>
242+ <td>
243+ メールタイトルによってオプションを上書きする場合にオプションの始まりを示す文字。<br />
244+ デフォルト: @
245+ </td>
246+</tr>
247+<tr>
248+ <td>オプションでblogidを指定する場合のキー</td>
249+ <td>
250+ メールタイトルによってオプションを上書きする場合に「Nucleus blog Id」を表すキー<br />
251+ デフォルト: b
252+ </td>
253+</tr>
254+<tr>
255+ <td>オプションでカテゴリを指定する場合のキー</td>
256+ <td>
257+ メールタイトルによってオプションを上書きする場合に「Nucleus カテゴリ名/カテゴリID」を表すキー<br />
258+ デフォルト: c
259+ </td>
260+</tr>
261+<tr>
262+ <td>オプションでストレートにpublish指定する場合のキー</td>
263+ <td>
264+ メールタイトルによってオプションを上書きする場合に「デフォルトで公開するか?」を表すキー<br />
265+ デフォルト: s
266+ </td>
267+</tr>
268+<tr>
269+ <td>追記にする場合の区切り文字(利用しない場合は空欄)</td>
270+ <td>
271+ メールの本文中に指定する文字列があった場合、にその文字列以降の部分が追記になります<br />
272+ デフォルト: (空欄)
273+ </td>
274+</tr>
275+<tr>
276+ <td>投稿許可アドレス(複数の場合改行で区切ってください)</td>
277+ <td>
278+ 投稿を許可するメールの送信元を指定します。ここで指定されない送信元からのメールは追加されません。<br />
279+ 推奨しませんが*だけを書いた行を追加することで、不特定のユーザから投稿することが可能になります。不用意に使うとblogがSPAMだらけになる可能性がありますので十分注意してください。<br />
280+ デフォルト: (空欄)
281+ </td>
282+</tr>
283+<tr>
284+ <td>投稿許可SubjectPrefix(制限無しの場合は空欄)</td>
285+ <td>
286+ Subjectが特定の文字列から始まる場合にのみ投稿が行われます。この機能を利用しないとき空欄にしておいてください。<br />
287+ デフォルト: (空欄)
288+ </td>
289+</tr>
290+<tr>
291+ <td>件名がないときの題名</td>
292+ <td>
293+ メールのタイトルがない場合にここで設定した内容をタイトルとして利用します<br />
294+ デフォルト: (空欄)
295+ </td>
296+</tr>
297+<tr>
298+ <td>htmlメールの場合に除去しないタグ</td>
299+ <td>
300+ メールをHTML形式で送信した際に、除去しないタグを指定します<br />
301+ デフォルト: &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;
302+ </td>
303+</tr>
304+<tr>
305+ <td>最大添付量(B)</td>
306+ <td>
307+ 添付ファイルを最大容量を指定します。これ以上の容量のファイルは追加されません。また、添付ファイルの最大容量はこれ以外の設定などによって制限を受ける場合があります<br />
308+ デフォルト: 300000
309+ </td>
310+</tr>
311+<tr>
312+ <td>対応MIMEタイプ(正規表現)</td>
313+ <td>
314+ 添付ファイルのMIMEタイプがここで指定するパターンに該当する場合にのみ保存を行います。<br />
315+ デフォルト: gif|jpe?g|png|bmp|octet-stream|x-pmd|x-mld|x-mid|x-smd|x-smaf|x-mpeg|pdf
316+ </td>
317+</tr>
318+<tr>
319+ <td>保存しないファイル(正規表現)</td>
320+ <td>
321+ 「対応MIMEタイプ」に該当する場合でも、ファイルがここで指定するパターンに該当する場合には添付ファイルは処理されません。<br />
322+ デフォルト: .+\.exe$|.+\.zip$|.+\.pif$|.+\.scr$
323+ </td>
324+</tr>
325+<tr>
326+ <td>画像ファイルの拡張子(正規表現)</td>
327+ <td>
328+ 「対応MIMEタイプ」に該当し、ファイルがここで指定するパターンに該当する場合に、添付ファイルが画像ファイルと認識します。それ以外はデータファイルとみなされます。<br />
329+ デフォルト: .+\.png$|.+\.jpe?g$|.+\.gif$|.+\.bmp$
330+ </td>
331+</tr>
332+<tr>
333+ <td>サムネイルを使用する?</td>
334+ <td>
335+ サムネイルを自動的に作成するか指定します<br />
336+ デフォルト: はい
337+ </td>
338+</tr>
339+<tr>
340+ <td>サムネイルの大きさ(Width)</td>
341+ <td>
342+ 生成されるサムネイルの最大横幅を指定します<br />
343+ デフォルト: 120
344+ </td>
345+</tr>
346+<tr>
347+ <td>サムネイルの大きさ(Hight)</td>
348+ <td>
349+ 生成されるサムネイルの最大縦幅を指定します<br />
350+ デフォルト: 120
351+ </td>
352+</tr>
353+<tr>
354+ <td>サムネイルを作る対象画像</td>
355+ <td>
356+ サムネイル生成の対象となるファイル名のパターンを指定します<br />
357+ デフォルト: .+\.jpe?g$|.+\.png$
358+ </td>
359+</tr>
360+<tr>
361+ <td>アイテム内に表示する画像の最大横幅</td>
362+ <td>
363+ サムネイルを生成しない場合に、IMGタグに挿入される横幅を指定します<br />
364+ デフォルト: 120
365+ </td>
366+</tr>
367+<tr>
368+ <td>テキストテンプレート</td>
369+ <td>
370+ 添付ファイルがない場合に使われるテンプレートです<br />
371+ テンプレート内で使えるタグについては別途解説してあります
372+ </td>
373+</tr>
374+<tr>
375+ <td>サムネイル付きテンプレート</td>
376+ <td>
377+ サムネイルを使うように設定してあり、なおかつサムネイルがきちんと生成できた場合に使われるテンプレートです<br />
378+ テンプレート内で使えるタグについては別途解説してあります
379+ </td>
380+</tr>
381+<tr>
382+ <td>サムネイルなしテンプレート</td>
383+ <td>
384+ サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超えない場合、もしくは「対応MIMEタイプ」に含まれるが「サムネイルを作る対象画像」に該当する場合に使われるテンプレートです。<br />
385+ テンプレート内で使えるタグについては別途解説してあります
386+ </td>
387+</tr>
388+<tr>
389+ <td>サムネイルなしテンプレート(縮小)</td>
390+ <td>
391+ サムネイルを使わないように設定していて、画像の横幅が「アイテム内に表示する画像の最大横幅」を超える場合に使われるテンプレートです<br />
392+ テンプレート内で使えるタグについては別途解説してあります
393+ </td>
394+</tr>
395+<tr>
396+ <td>データファイルテンプレート</td>
397+ <td>
398+ 画像以外の添付ファイルがあるときに使われるテンプレートです<br />
399+ テンプレート内で使えるタグについては別途解説してあります
400+ </td>
401+</tr>
402+</table>
403+
404+<h3>テンプレート内で利用可能なタグ</h3>
405+
406+<p>テンプレート中には以下のタグが利用できます。</p>
407+<p>(テンプレートによっては使えないものもあります)</p>
408+
409+<table>
410+<tr>
411+ <th>タグ</th>
412+ <th>解説</th>
413+</tr>
414+<tr>
415+ <td>&lt;%sizeW%&gt;</td>
416+ <td>オリジナル画像の横幅</td>
417+</tr>
418+<tr>
419+ <td>&lt;%sizeH%&gt;</td>
420+ <td>オリジナル画像の縦幅</td>
421+</tr>
422+<tr>
423+ <td>&lt;%thumbW%&gt;</td>
424+ <td>生成されたサムネイルの横幅</td>
425+</tr>
426+<tr>
427+ <td>&lt;%thumbH%&gt;</td>
428+ <td>生成されたサムネイルの縦幅</td>
429+</tr>
430+<tr>
431+ <td>&lt;%reductionW%&gt;</td>
432+ <td>画像を縮小表示するときの横幅</td>
433+</tr>
434+<tr>
435+ <td>&lt;%reductionH%&gt;</td>
436+ <td>画像を縮小表示するときの縦幅</td>
437+</tr>
438+<tr>
439+ <td>&lt;%thumbUrl%&gt;</td>
440+ <td>生成されたサムネイルのURL</td>
441+</tr>
442+<tr>
443+ <td>&lt;%imageUrl%&gt;</td>
444+ <td>オリジナル画像(添付ファイル)のURL</td>
445+</tr>
446+<tr>
447+ <td>&lt;%mediaUrl%&gt;</td>
448+ <td>mediaディレクトリのURL</td>
449+</tr>
450+<tr>
451+ <td>&lt;%fileName%&gt;</td>
452+ <td>添付ファイルのファイル名</td>
453+</tr>
454+<tr>
455+ <td>&lt;%body%&gt;</td>
456+ <td>メールの本文</td>
457+</tr>
458+</table>
459+
460+<h3>サポートとバグレポート</h3>
461+
462+<p>問題が解決できない場合には<a href="http://japan.nucleuscms.org/bb/">Nucleus(JP)フォーラム</a>を活用しましょう。</p>
463+<p>バグレポートについては配布元の<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>にて受け付けていますので、該当のバージョンのエントリにコメント又はトラックバックでどうぞ。</p>
464+<p>「動作確認しました」というだけでも開発者には重要な情報になります。</p>
465+
466+<h3>アンインストール</h3>
467+
468+<p><code>NP_Moblog</code>を完全にアンインストールするための手順は以下の通りです</p>
469+
470+<ol>
471+ <li>「プラグイン管理」からNP_Moblogをアンインストールする</li>
472+ <li>Nucleusをインストールしているサーバの<code>nucleus/plugin/</code>ディレクトリから<code>NP_Moblog.php</code>と<code>moblog</code>ディレクトリを削除する</li>
473+</ol>
474+
475+<h3>バージョン履歴</h3>
476+
477+<p>新バージョンは<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>で確認してください。</p>
478+
479+<ul>
480+ <!--
481+ <li> //TODO: [Fixed] 日本語のファイル添付に対応 </li>
482+ -->
483+
484+ <li>Version 1.17: (2010/06/06)</li>
485+ <li> [Fixed] エンコーディングが適切に検出されない問題を修正</li>
486+ <li> [Fixed] mysql_query()をsql_query()に変更</li>
487+ <li> [Fixed] eregi()をpreg_match()に変更</li>
488+ <li> [Fixed] register_globals,allow_url_fopen,allow_url_includeがonの場合にリモートコードインジェクションが発生する問題に対応しました (Thanks Katsumiさん)</li>
489+ <li>Version 1.16: (2007/03/17)</li>
490+ <li> [Fixed] メールアドレスの大文字小文字を区別しないようにした</li>
491+ <li> [Fixed] 管理者が設定の変更を行った場合、useridディレクトリにきちんと画像が保存されない問題を修正</li>
492+ <li>Version 1.15: (2006/09/30)</li>
493+ <li> [Fixed] セキュリティの向上</li>
494+ <li>Version 1.14: (2006/07/15)</li>
495+ <li> [Added] 画像・サムネイルの保存先を指定できるようにした</li>
496+ <li> [Fixed] ファイル名のPrefixをuniqid()からYYYYmmddHHiissnn形式(日付+連番)に変更した</li>
497+ <li> [Fixed] ユーザーがSuper-Adminである場合に任意のBlog、カテゴリで投稿できる問題を修正</li>
498+ <li> [Fixed] ブログ名、カテゴリ名に特定の文字が入っている場合に、投稿が出来ない問題を修正</li>
499+ <li>Version 1.13: (2006/03/24)</li>
500+ <li> [Fixed] HTMLメールが上手く扱えない問題を修正</li>
501+ <li>Version 1.12: (2005/03/19)</li>
502+ <li> [Fixed] ライセンスを変更した</li>
503+ <li>Version 1.11: (2005/12/30)</li>
504+ <li> [Fixed] マニュアルの誤記、XHTMLに準拠していなかった部分を修正</li>
505+ <li> [Fixed] PEARファイル群のinclude()の方法を改善</li>
506+ <li>Version 1.10: (2005/09/01)</li>
507+ <li> [Fixed] 「イメージ添付メールのみ追加?」が「はい」の場合に、添付ファイルが存在しているにもかかわらず、エントリが追加されない不具合を修正</li>
508+ <li> [Added] 管理画面から動作確認ができる機能を追加</li>
509+ <li>Version 1.9: (2005/09/01)</li>
510+ <li> [Changed] 「Nucleus blog Id」「Nucleus カテゴリ名/カテゴリID」を「Nucleusカテゴリ名(Blog名)」変更</li>
511+ <li>Version 1.8: (2005/07/25)</li>
512+ <li> [Fixed] PEARクラスのinclude()方法を変更した</li>
513+ <li>Version 1.7: (2005/07/07)</li>
514+ <li> [Fixed] 1.6で修正されていなかったオプション分割の不具合を修正</li>
515+ <li>Version 1.6: (2005/07/02)</li>
516+ <li> [Added] テキストテンプレート(添付ファイルがない場合のテンプレート)を追加</li>
517+ <li> [Added] SPAM Checkに対応(別途、NP_BlackListなどが必要です)</li>
518+ <li> [Added] Help(このファイル)を同梱</li>
519+ <li> [Fixed] オプション分割の不具合を修正</li>
520+ <li> [Fixed] Nucleus v3.2以前のバージョンにインストールできないようにした。(getMinNucleusVersionを設定)</li>
521+</ul>
522+
523+<h3>Special Thanks</h3>
524+
525+<p>NP_Moblogははまみおさんの<a href="http://nakahara21.com/?itemid=133">NP_HeelloWorld v0.8</a>を基に開発しました。原作者のまみおさんに感謝します。</p>
526+
527+<h3>開発者について</h3>
528+
529+<ul>
530+ <li><a href="http://blog.cles.jp/member/1">hsur</a> (<a href="http://blog.cles.jp/">cles::blog</a>)</li>
531+ <li>配布元: <a href="http://blog.cles.jp/np_cles/category/31/subcatid/2">NP_cles() - NP_Moblog</a></li>
532+ <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>
533+ <li>ドネーションや仕事のご依頼も歓迎します。</li>
534+</ul>
--- a/trunk/NP_Moblog/moblog/index.php
+++ b/trunk/NP_Moblog/moblog/index.php
@@ -1,103 +1,103 @@
1-<?php
2-// vim: tabstop=2:shiftwidth=2
3-
4-/**
5- * index.php ($Revision: 1.1 $)
6- *
7- * by hsur ( http://blog.cles.jp/np_cles )
8- * $Id: index.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
9-*/
10-
11-/*
12- * Copyright (C) 2004-2006 cles. All rights reserved.
13- *
14- * This program is free software; you can redistribute it and/or
15- * modify it under the terms of the GNU General Public License
16- * as published by the Free Software Foundation; either version 2
17- * of the License, or (at your option) any later version.
18- *
19- * This program is distributed in the hope that it will be useful,
20- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22- * GNU General Public License for more details.
23- *
24- * You should have received a copy of the GNU General Public License
25- * along with this program; if not, write to the Free Software
26- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27- *
28- * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29- * permission to link the code of this program with those files in the PEAR
30- * library that are licensed under the PHP License (or with modified versions
31- * of those files that use the same license as those files), and distribute
32- * linked combinations including the two. You must obey the GNU General Public
33- * License in all respects for all of the code used other than those files in
34- * the PEAR library that are licensed under the PHP License. If you modify
35- * this file, you may extend this exception to your version of the file,
36- * but you are not obligated to do so. If you do not wish to do so, delete
37- * this exception statement from your version.
38-*/
39-
40-$strRel = '../../../';
41-include ($strRel.'config.php');
42-include ($DIR_LIBS.'PLUGINADMIN.php');
43-
44-require_once($DIR_PLUGINS . 'sharedlibs/sharedlibs.php');
45-require_once('cles/Feedback.php');
46-
47-$required = '4.3.0';
48-if( ! version_compare(phpversion() , $required , '>=') ){
49- ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
50-}
51-
52-if ($blogid) {
53- $isblogadmin = $member->isBlogAdmin($blogid);
54-} else
55- $isblogadmin = 0;
56-
57-if (!$member->isLoggedIn()) {
58- $oPluginAdmin = new PluginAdmin('Moblog');
59- $oPluginAdmin->start();
60- echo "<p>ログインが必要です</p>";
61- $oPluginAdmin->end();
62- exit;
63-}
64-
65-if (!($member->isAdmin() || $isblogadmin)) {
66- $oPluginAdmin = new PluginAdmin('Moblog');
67- $oPluginAdmin->start();
68- echo "<p>"._ERROR_DISALLOWED."</p>";
69- $oPluginAdmin->end();
70- exit;
71-}
72-
73-if (isset ($_GET['page'])) {
74- $action = getVar('page');
75-}
76-if (isset ($_POST['page'])) {
77- $action = getVar('page');
78-}
79-
80-// create the admin area page
81-$oPluginAdmin = new PluginAdmin('Moblog');
82-$oPluginAdmin->start();
83-$fb =& new cles_Feedback($oPluginAdmin);
84-
85-// menu
86-echo "<h2>Moblog menu</h2>\n";
87-echo "<ul>\n";
88-echo "<li><a href=\"".serverVar('PHP_SELF')."?page=report\"><span style=\"font-weight:bold; color:red\">" . $fb->getMenuStr() . "</span></a></li>\n";
89-echo "</ul>\n";
90-
91-//action
92-switch ($action) {
93- case 'report' :
94- $fb->printForm();
95- break;
96-
97- default :
98- break;
99-}
100-
101-echo "<br />";
102-
103-$oPluginAdmin->end();
1+<?php
2+// vim: tabstop=2:shiftwidth=2
3+
4+/**
5+ * index.php ($Revision: 1.21 $)
6+ *
7+ * by hsur ( http://blog.cles.jp/np_cles )
8+ * $Id: index.php,v 1.21 2010/06/06 11:44:19 hsur Exp $
9+*/
10+
11+/*
12+ * Copyright (C) 2004-2010 cles. All rights reserved.
13+ *
14+ * This program is free software; you can redistribute it and/or
15+ * modify it under the terms of the GNU General Public License
16+ * as published by the Free Software Foundation; either version 2
17+ * of the License, or (at your option) any later version.
18+ *
19+ * This program is distributed in the hope that it will be useful,
20+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+ * GNU General Public License for more details.
23+ *
24+ * You should have received a copy of the GNU General Public License
25+ * along with this program; if not, write to the Free Software
26+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27+ *
28+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29+ * permission to link the code of this program with those files in the PEAR
30+ * library that are licensed under the PHP License (or with modified versions
31+ * of those files that use the same license as those files), and distribute
32+ * linked combinations including the two. You must obey the GNU General Public
33+ * License in all respects for all of the code used other than those files in
34+ * the PEAR library that are licensed under the PHP License. If you modify
35+ * this file, you may extend this exception to your version of the file,
36+ * but you are not obligated to do so. If you do not wish to do so, delete
37+ * this exception statement from your version.
38+*/
39+
40+$strRel = '../../../';
41+include ($strRel.'config.php');
42+include ($DIR_LIBS.'PLUGINADMIN.php');
43+
44+require_once($DIR_PLUGINS . 'sharedlibs/sharedlibs.php');
45+require_once('cles/Feedback.php');
46+
47+$required = '4.3.0';
48+if( ! version_compare(phpversion() , $required , '>=') ){
49+ ACTIONLOG :: add(WARNING, 'NP_MoblogはPHP>=4.3.0であることが必要です。');
50+}
51+
52+if ($blogid) {
53+ $isblogadmin = $member->isBlogAdmin($blogid);
54+} else
55+ $isblogadmin = 0;
56+
57+if (!$member->isLoggedIn()) {
58+ $oPluginAdmin = new PluginAdmin('Moblog');
59+ $oPluginAdmin->start();
60+ echo "<p>ログインが必要です</p>";
61+ $oPluginAdmin->end();
62+ exit;
63+}
64+
65+if (!($member->isAdmin() || $isblogadmin)) {
66+ $oPluginAdmin = new PluginAdmin('Moblog');
67+ $oPluginAdmin->start();
68+ echo "<p>"._ERROR_DISALLOWED."</p>";
69+ $oPluginAdmin->end();
70+ exit;
71+}
72+
73+if (isset ($_GET['page'])) {
74+ $action = getVar('page');
75+}
76+if (isset ($_POST['page'])) {
77+ $action = getVar('page');
78+}
79+
80+// create the admin area page
81+$oPluginAdmin = new PluginAdmin('Moblog');
82+$oPluginAdmin->start();
83+$fb =& new cles_Feedback($oPluginAdmin);
84+
85+// menu
86+echo "<h2>Moblog menu</h2>\n";
87+echo "<ul>\n";
88+echo "<li><a href=\"".serverVar('PHP_SELF')."?page=report\"><span style=\"font-weight:bold; color:red\">" . $fb->getMenuStr() . "</span></a></li>\n";
89+echo "</ul>\n";
90+
91+//action
92+switch ($action) {
93+ case 'report' :
94+ $fb->printForm();
95+ break;
96+
97+ default :
98+ break;
99+}
100+
101+echo "<br />";
102+
103+$oPluginAdmin->end();
--- a/trunk/NP_Moblog/sharedlibs/Mail/RFC822.php
+++ b/trunk/NP_Moblog/sharedlibs/Mail/RFC822.php
@@ -1,923 +1,923 @@
1-<?php
2-// +-----------------------------------------------------------------------+
3-// | Copyright (c) 2001-2002, Richard Heyes |
4-// | All rights reserved. |
5-// | |
6-// | Redistribution and use in source and binary forms, with or without |
7-// | modification, are permitted provided that the following conditions |
8-// | are met: |
9-// | |
10-// | o Redistributions of source code must retain the above copyright |
11-// | notice, this list of conditions and the following disclaimer. |
12-// | o Redistributions in binary form must reproduce the above copyright |
13-// | notice, this list of conditions and the following disclaimer in the |
14-// | documentation and/or other materials provided with the distribution.|
15-// | o The names of the authors may not be used to endorse or promote |
16-// | products derived from this software without specific prior written |
17-// | permission. |
18-// | |
19-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30-// | |
31-// +-----------------------------------------------------------------------+
32-// | Authors: Richard Heyes <richard@phpguru.org> |
33-// | Chuck Hagenbuch <chuck@horde.org> |
34-// +-----------------------------------------------------------------------+
35-
36-/**
37- * RFC 822 Email address list validation Utility
38- *
39- * What is it?
40- *
41- * This class will take an address string, and parse it into it's consituent
42- * parts, be that either addresses, groups, or combinations. Nested groups
43- * are not supported. The structure it returns is pretty straight forward,
44- * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
45- * print_r() to view the structure.
46- *
47- * How do I use it?
48- *
49- * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
50- * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
51- * print_r($structure);
52- *
53- * @author Richard Heyes <richard@phpguru.org>
54- * @author Chuck Hagenbuch <chuck@horde.org>
55- * @version $Revision: 1.1 $
56- * @license BSD
57- * @package Mail
58- */
59-class Mail_RFC822 {
60-
61- /**
62- * The address being parsed by the RFC822 object.
63- * @var string $address
64- */
65- var $address = '';
66-
67- /**
68- * The default domain to use for unqualified addresses.
69- * @var string $default_domain
70- */
71- var $default_domain = 'localhost';
72-
73- /**
74- * Should we return a nested array showing groups, or flatten everything?
75- * @var boolean $nestGroups
76- */
77- var $nestGroups = true;
78-
79- /**
80- * Whether or not to validate atoms for non-ascii characters.
81- * @var boolean $validate
82- */
83- var $validate = true;
84-
85- /**
86- * The array of raw addresses built up as we parse.
87- * @var array $addresses
88- */
89- var $addresses = array();
90-
91- /**
92- * The final array of parsed address information that we build up.
93- * @var array $structure
94- */
95- var $structure = array();
96-
97- /**
98- * The current error message, if any.
99- * @var string $error
100- */
101- var $error = null;
102-
103- /**
104- * An internal counter/pointer.
105- * @var integer $index
106- */
107- var $index = null;
108-
109- /**
110- * The number of groups that have been found in the address list.
111- * @var integer $num_groups
112- * @access public
113- */
114- var $num_groups = 0;
115-
116- /**
117- * A variable so that we can tell whether or not we're inside a
118- * Mail_RFC822 object.
119- * @var boolean $mailRFC822
120- */
121- var $mailRFC822 = true;
122-
123- /**
124- * A limit after which processing stops
125- * @var int $limit
126- */
127- var $limit = null;
128-
129- /**
130- * Sets up the object. The address must either be set here or when
131- * calling parseAddressList(). One or the other.
132- *
133- * @access public
134- * @param string $address The address(es) to validate.
135- * @param string $default_domain Default domain/host etc. If not supplied, will be set to localhost.
136- * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
137- * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
138- *
139- * @return object Mail_RFC822 A new Mail_RFC822 object.
140- */
141- function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
142- {
143- if (isset($address)) $this->address = $address;
144- if (isset($default_domain)) $this->default_domain = $default_domain;
145- if (isset($nest_groups)) $this->nestGroups = $nest_groups;
146- if (isset($validate)) $this->validate = $validate;
147- if (isset($limit)) $this->limit = $limit;
148- }
149-
150- /**
151- * Starts the whole process. The address must either be set here
152- * or when creating the object. One or the other.
153- *
154- * @access public
155- * @param string $address The address(es) to validate.
156- * @param string $default_domain Default domain/host etc.
157- * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
158- * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
159- *
160- * @return array A structured array of addresses.
161- */
162- function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
163- {
164- if (!isset($this) || !isset($this->mailRFC822)) {
165- $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
166- return $obj->parseAddressList();
167- }
168-
169- if (isset($address)) $this->address = $address;
170- if (isset($default_domain)) $this->default_domain = $default_domain;
171- if (isset($nest_groups)) $this->nestGroups = $nest_groups;
172- if (isset($validate)) $this->validate = $validate;
173- if (isset($limit)) $this->limit = $limit;
174-
175- $this->structure = array();
176- $this->addresses = array();
177- $this->error = null;
178- $this->index = null;
179-
180- // Unfold any long lines in $this->address.
181- $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
182- $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
183-
184- while ($this->address = $this->_splitAddresses($this->address));
185-
186- if ($this->address === false || isset($this->error)) {
187- require_once 'PEAR.php';
188- return PEAR::raiseError($this->error);
189- }
190-
191- // Validate each address individually. If we encounter an invalid
192- // address, stop iterating and return an error immediately.
193- foreach ($this->addresses as $address) {
194- $valid = $this->_validateAddress($address);
195-
196- if ($valid === false || isset($this->error)) {
197- require_once 'PEAR.php';
198- return PEAR::raiseError($this->error);
199- }
200-
201- if (!$this->nestGroups) {
202- $this->structure = array_merge($this->structure, $valid);
203- } else {
204- $this->structure[] = $valid;
205- }
206- }
207-
208- return $this->structure;
209- }
210-
211- /**
212- * Splits an address into separate addresses.
213- *
214- * @access private
215- * @param string $address The addresses to split.
216- * @return boolean Success or failure.
217- */
218- function _splitAddresses($address)
219- {
220- if (!empty($this->limit) && count($this->addresses) == $this->limit) {
221- return '';
222- }
223-
224- if ($this->_isGroup($address) && !isset($this->error)) {
225- $split_char = ';';
226- $is_group = true;
227- } elseif (!isset($this->error)) {
228- $split_char = ',';
229- $is_group = false;
230- } elseif (isset($this->error)) {
231- return false;
232- }
233-
234- // Split the string based on the above ten or so lines.
235- $parts = explode($split_char, $address);
236- $string = $this->_splitCheck($parts, $split_char);
237-
238- // If a group...
239- if ($is_group) {
240- // If $string does not contain a colon outside of
241- // brackets/quotes etc then something's fubar.
242-
243- // First check there's a colon at all:
244- if (strpos($string, ':') === false) {
245- $this->error = 'Invalid address: ' . $string;
246- return false;
247- }
248-
249- // Now check it's outside of brackets/quotes:
250- if (!$this->_splitCheck(explode(':', $string), ':')) {
251- return false;
252- }
253-
254- // We must have a group at this point, so increase the counter:
255- $this->num_groups++;
256- }
257-
258- // $string now contains the first full address/group.
259- // Add to the addresses array.
260- $this->addresses[] = array(
261- 'address' => trim($string),
262- 'group' => $is_group
263- );
264-
265- // Remove the now stored address from the initial line, the +1
266- // is to account for the explode character.
267- $address = trim(substr($address, strlen($string) + 1));
268-
269- // If the next char is a comma and this was a group, then
270- // there are more addresses, otherwise, if there are any more
271- // chars, then there is another address.
272- if ($is_group && substr($address, 0, 1) == ','){
273- $address = trim(substr($address, 1));
274- return $address;
275-
276- } elseif (strlen($address) > 0) {
277- return $address;
278-
279- } else {
280- return '';
281- }
282-
283- // If you got here then something's off
284- return false;
285- }
286-
287- /**
288- * Checks for a group at the start of the string.
289- *
290- * @access private
291- * @param string $address The address to check.
292- * @return boolean Whether or not there is a group at the start of the string.
293- */
294- function _isGroup($address)
295- {
296- // First comma not in quotes, angles or escaped:
297- $parts = explode(',', $address);
298- $string = $this->_splitCheck($parts, ',');
299-
300- // Now we have the first address, we can reliably check for a
301- // group by searching for a colon that's not escaped or in
302- // quotes or angle brackets.
303- if (count($parts = explode(':', $string)) > 1) {
304- $string2 = $this->_splitCheck($parts, ':');
305- return ($string2 !== $string);
306- } else {
307- return false;
308- }
309- }
310-
311- /**
312- * A common function that will check an exploded string.
313- *
314- * @access private
315- * @param array $parts The exloded string.
316- * @param string $char The char that was exploded on.
317- * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
318- */
319- function _splitCheck($parts, $char)
320- {
321- $string = $parts[0];
322-
323- for ($i = 0; $i < count($parts); $i++) {
324- if ($this->_hasUnclosedQuotes($string)
325- || $this->_hasUnclosedBrackets($string, '<>')
326- || $this->_hasUnclosedBrackets($string, '[]')
327- || $this->_hasUnclosedBrackets($string, '()')
328- || substr($string, -1) == '\\') {
329- if (isset($parts[$i + 1])) {
330- $string = $string . $char . $parts[$i + 1];
331- } else {
332- $this->error = 'Invalid address spec. Unclosed bracket or quotes';
333- return false;
334- }
335- } else {
336- $this->index = $i;
337- break;
338- }
339- }
340-
341- return $string;
342- }
343-
344- /**
345- * Checks if a string has an unclosed quotes or not.
346- *
347- * @access private
348- * @param string $string The string to check.
349- * @return boolean True if there are unclosed quotes inside the string, false otherwise.
350- */
351- function _hasUnclosedQuotes($string)
352- {
353- $string = explode('"', $string);
354- $string_cnt = count($string);
355-
356- for ($i = 0; $i < (count($string) - 1); $i++)
357- if (substr($string[$i], -1) == '\\')
358- $string_cnt--;
359-
360- return ($string_cnt % 2 === 0);
361- }
362-
363- /**
364- * Checks if a string has an unclosed brackets or not. IMPORTANT:
365- * This function handles both angle brackets and square brackets;
366- *
367- * @access private
368- * @param string $string The string to check.
369- * @param string $chars The characters to check for.
370- * @return boolean True if there are unclosed brackets inside the string, false otherwise.
371- */
372- function _hasUnclosedBrackets($string, $chars)
373- {
374- $num_angle_start = substr_count($string, $chars[0]);
375- $num_angle_end = substr_count($string, $chars[1]);
376-
377- $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
378- $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
379-
380- if ($num_angle_start < $num_angle_end) {
381- $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
382- return false;
383- } else {
384- return ($num_angle_start > $num_angle_end);
385- }
386- }
387-
388- /**
389- * Sub function that is used only by hasUnclosedBrackets().
390- *
391- * @access private
392- * @param string $string The string to check.
393- * @param integer &$num The number of occurences.
394- * @param string $char The character to count.
395- * @return integer The number of occurences of $char in $string, adjusted for backslashes.
396- */
397- function _hasUnclosedBracketsSub($string, &$num, $char)
398- {
399- $parts = explode($char, $string);
400- for ($i = 0; $i < count($parts); $i++){
401- if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
402- $num--;
403- if (isset($parts[$i + 1]))
404- $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
405- }
406-
407- return $num;
408- }
409-
410- /**
411- * Function to begin checking the address.
412- *
413- * @access private
414- * @param string $address The address to validate.
415- * @return mixed False on failure, or a structured array of address information on success.
416- */
417- function _validateAddress($address)
418- {
419- $is_group = false;
420- $addresses = array();
421-
422- if ($address['group']) {
423- $is_group = true;
424-
425- // Get the group part of the name
426- $parts = explode(':', $address['address']);
427- $groupname = $this->_splitCheck($parts, ':');
428- $structure = array();
429-
430- // And validate the group part of the name.
431- if (!$this->_validatePhrase($groupname)){
432- $this->error = 'Group name did not validate.';
433- return false;
434- } else {
435- // Don't include groups if we are not nesting
436- // them. This avoids returning invalid addresses.
437- if ($this->nestGroups) {
438- $structure = new stdClass;
439- $structure->groupname = $groupname;
440- }
441- }
442-
443- $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
444- }
445-
446- // If a group then split on comma and put into an array.
447- // Otherwise, Just put the whole address in an array.
448- if ($is_group) {
449- while (strlen($address['address']) > 0) {
450- $parts = explode(',', $address['address']);
451- $addresses[] = $this->_splitCheck($parts, ',');
452- $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
453- }
454- } else {
455- $addresses[] = $address['address'];
456- }
457-
458- // Check that $addresses is set, if address like this:
459- // Groupname:;
460- // Then errors were appearing.
461- if (!count($addresses)){
462- $this->error = 'Empty group.';
463- return false;
464- }
465-
466- // Trim the whitespace from all of the address strings.
467- array_map('trim', $addresses);
468-
469- // Validate each mailbox.
470- // Format could be one of: name <geezer@domain.com>
471- // geezer@domain.com
472- // geezer
473- // ... or any other format valid by RFC 822.
474- for ($i = 0; $i < count($addresses); $i++) {
475- if (!$this->validateMailbox($addresses[$i])) {
476- if (empty($this->error)) {
477- $this->error = 'Validation failed for: ' . $addresses[$i];
478- }
479- return false;
480- }
481- }
482-
483- // Nested format
484- if ($this->nestGroups) {
485- if ($is_group) {
486- $structure->addresses = $addresses;
487- } else {
488- $structure = $addresses[0];
489- }
490-
491- // Flat format
492- } else {
493- if ($is_group) {
494- $structure = array_merge($structure, $addresses);
495- } else {
496- $structure = $addresses;
497- }
498- }
499-
500- return $structure;
501- }
502-
503- /**
504- * Function to validate a phrase.
505- *
506- * @access private
507- * @param string $phrase The phrase to check.
508- * @return boolean Success or failure.
509- */
510- function _validatePhrase($phrase)
511- {
512- // Splits on one or more Tab or space.
513- $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
514-
515- $phrase_parts = array();
516- while (count($parts) > 0){
517- $phrase_parts[] = $this->_splitCheck($parts, ' ');
518- for ($i = 0; $i < $this->index + 1; $i++)
519- array_shift($parts);
520- }
521-
522- foreach ($phrase_parts as $part) {
523- // If quoted string:
524- if (substr($part, 0, 1) == '"') {
525- if (!$this->_validateQuotedString($part)) {
526- return false;
527- }
528- continue;
529- }
530-
531- // Otherwise it's an atom:
532- if (!$this->_validateAtom($part)) return false;
533- }
534-
535- return true;
536- }
537-
538- /**
539- * Function to validate an atom which from rfc822 is:
540- * atom = 1*<any CHAR except specials, SPACE and CTLs>
541- *
542- * If validation ($this->validate) has been turned off, then
543- * validateAtom() doesn't actually check anything. This is so that you
544- * can split a list of addresses up before encoding personal names
545- * (umlauts, etc.), for example.
546- *
547- * @access private
548- * @param string $atom The string to check.
549- * @return boolean Success or failure.
550- */
551- function _validateAtom($atom)
552- {
553- if (!$this->validate) {
554- // Validation has been turned off; assume the atom is okay.
555- return true;
556- }
557-
558- // Check for any char from ASCII 0 - ASCII 127
559- if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
560- return false;
561- }
562-
563- // Check for specials:
564- if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
565- return false;
566- }
567-
568- // Check for control characters (ASCII 0-31):
569- if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
570- return false;
571- }
572-
573- return true;
574- }
575-
576- /**
577- * Function to validate quoted string, which is:
578- * quoted-string = <"> *(qtext/quoted-pair) <">
579- *
580- * @access private
581- * @param string $qstring The string to check
582- * @return boolean Success or failure.
583- */
584- function _validateQuotedString($qstring)
585- {
586- // Leading and trailing "
587- $qstring = substr($qstring, 1, -1);
588-
589- // Perform check, removing quoted characters first.
590- return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
591- }
592-
593- /**
594- * Function to validate a mailbox, which is:
595- * mailbox = addr-spec ; simple address
596- * / phrase route-addr ; name and route-addr
597- *
598- * @access public
599- * @param string &$mailbox The string to check.
600- * @return boolean Success or failure.
601- */
602- function validateMailbox(&$mailbox)
603- {
604- // A couple of defaults.
605- $phrase = '';
606- $comment = '';
607- $comments = array();
608-
609- // Catch any RFC822 comments and store them separately.
610- $_mailbox = $mailbox;
611- while (strlen(trim($_mailbox)) > 0) {
612- $parts = explode('(', $_mailbox);
613- $before_comment = $this->_splitCheck($parts, '(');
614- if ($before_comment != $_mailbox) {
615- // First char should be a (.
616- $comment = substr(str_replace($before_comment, '', $_mailbox), 1);
617- $parts = explode(')', $comment);
618- $comment = $this->_splitCheck($parts, ')');
619- $comments[] = $comment;
620-
621- // +1 is for the trailing )
622- $_mailbox = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);
623- } else {
624- break;
625- }
626- }
627-
628- foreach ($comments as $comment) {
629- $mailbox = str_replace("($comment)", '', $mailbox);
630- }
631-
632- $mailbox = trim($mailbox);
633-
634- // Check for name + route-addr
635- if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
636- $parts = explode('<', $mailbox);
637- $name = $this->_splitCheck($parts, '<');
638-
639- $phrase = trim($name);
640- $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
641-
642- if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
643- return false;
644- }
645-
646- // Only got addr-spec
647- } else {
648- // First snip angle brackets if present.
649- if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
650- $addr_spec = substr($mailbox, 1, -1);
651- } else {
652- $addr_spec = $mailbox;
653- }
654-
655- if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
656- return false;
657- }
658- }
659-
660- // Construct the object that will be returned.
661- $mbox = new stdClass();
662-
663- // Add the phrase (even if empty) and comments
664- $mbox->personal = $phrase;
665- $mbox->comment = isset($comments) ? $comments : array();
666-
667- if (isset($route_addr)) {
668- $mbox->mailbox = $route_addr['local_part'];
669- $mbox->host = $route_addr['domain'];
670- $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
671- } else {
672- $mbox->mailbox = $addr_spec['local_part'];
673- $mbox->host = $addr_spec['domain'];
674- }
675-
676- $mailbox = $mbox;
677- return true;
678- }
679-
680- /**
681- * This function validates a route-addr which is:
682- * route-addr = "<" [route] addr-spec ">"
683- *
684- * Angle brackets have already been removed at the point of
685- * getting to this function.
686- *
687- * @access private
688- * @param string $route_addr The string to check.
689- * @return mixed False on failure, or an array containing validated address/route information on success.
690- */
691- function _validateRouteAddr($route_addr)
692- {
693- // Check for colon.
694- if (strpos($route_addr, ':') !== false) {
695- $parts = explode(':', $route_addr);
696- $route = $this->_splitCheck($parts, ':');
697- } else {
698- $route = $route_addr;
699- }
700-
701- // If $route is same as $route_addr then the colon was in
702- // quotes or brackets or, of course, non existent.
703- if ($route === $route_addr){
704- unset($route);
705- $addr_spec = $route_addr;
706- if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
707- return false;
708- }
709- } else {
710- // Validate route part.
711- if (($route = $this->_validateRoute($route)) === false) {
712- return false;
713- }
714-
715- $addr_spec = substr($route_addr, strlen($route . ':'));
716-
717- // Validate addr-spec part.
718- if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
719- return false;
720- }
721- }
722-
723- if (isset($route)) {
724- $return['adl'] = $route;
725- } else {
726- $return['adl'] = '';
727- }
728-
729- $return = array_merge($return, $addr_spec);
730- return $return;
731- }
732-
733- /**
734- * Function to validate a route, which is:
735- * route = 1#("@" domain) ":"
736- *
737- * @access private
738- * @param string $route The string to check.
739- * @return mixed False on failure, or the validated $route on success.
740- */
741- function _validateRoute($route)
742- {
743- // Split on comma.
744- $domains = explode(',', trim($route));
745-
746- foreach ($domains as $domain) {
747- $domain = str_replace('@', '', trim($domain));
748- if (!$this->_validateDomain($domain)) return false;
749- }
750-
751- return $route;
752- }
753-
754- /**
755- * Function to validate a domain, though this is not quite what
756- * you expect of a strict internet domain.
757- *
758- * domain = sub-domain *("." sub-domain)
759- *
760- * @access private
761- * @param string $domain The string to check.
762- * @return mixed False on failure, or the validated domain on success.
763- */
764- function _validateDomain($domain)
765- {
766- // Note the different use of $subdomains and $sub_domains
767- $subdomains = explode('.', $domain);
768-
769- while (count($subdomains) > 0) {
770- $sub_domains[] = $this->_splitCheck($subdomains, '.');
771- for ($i = 0; $i < $this->index + 1; $i++)
772- array_shift($subdomains);
773- }
774-
775- foreach ($sub_domains as $sub_domain) {
776- if (!$this->_validateSubdomain(trim($sub_domain)))
777- return false;
778- }
779-
780- // Managed to get here, so return input.
781- return $domain;
782- }
783-
784- /**
785- * Function to validate a subdomain:
786- * subdomain = domain-ref / domain-literal
787- *
788- * @access private
789- * @param string $subdomain The string to check.
790- * @return boolean Success or failure.
791- */
792- function _validateSubdomain($subdomain)
793- {
794- if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
795- if (!$this->_validateDliteral($arr[1])) return false;
796- } else {
797- if (!$this->_validateAtom($subdomain)) return false;
798- }
799-
800- // Got here, so return successful.
801- return true;
802- }
803-
804- /**
805- * Function to validate a domain literal:
806- * domain-literal = "[" *(dtext / quoted-pair) "]"
807- *
808- * @access private
809- * @param string $dliteral The string to check.
810- * @return boolean Success or failure.
811- */
812- function _validateDliteral($dliteral)
813- {
814- return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
815- }
816-
817- /**
818- * Function to validate an addr-spec.
819- *
820- * addr-spec = local-part "@" domain
821- *
822- * @access private
823- * @param string $addr_spec The string to check.
824- * @return mixed False on failure, or the validated addr-spec on success.
825- */
826- function _validateAddrSpec($addr_spec)
827- {
828- $addr_spec = trim($addr_spec);
829-
830- // Split on @ sign if there is one.
831- if (strpos($addr_spec, '@') !== false) {
832- $parts = explode('@', $addr_spec);
833- $local_part = $this->_splitCheck($parts, '@');
834- $domain = substr($addr_spec, strlen($local_part . '@'));
835-
836- // No @ sign so assume the default domain.
837- } else {
838- $local_part = $addr_spec;
839- $domain = $this->default_domain;
840- }
841-
842- if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
843- if (($domain = $this->_validateDomain($domain)) === false) return false;
844-
845- // Got here so return successful.
846- return array('local_part' => $local_part, 'domain' => $domain);
847- }
848-
849- /**
850- * Function to validate the local part of an address:
851- * local-part = word *("." word)
852- *
853- * @access private
854- * @param string $local_part
855- * @return mixed False on failure, or the validated local part on success.
856- */
857- function _validateLocalPart($local_part)
858- {
859- $parts = explode('.', $local_part);
860- $words = array();
861-
862- // Split the local_part into words.
863- while (count($parts) > 0){
864- $words[] = $this->_splitCheck($parts, '.');
865- for ($i = 0; $i < $this->index + 1; $i++) {
866- array_shift($parts);
867- }
868- }
869-
870- // Validate each word.
871- foreach ($words as $word) {
872- // If this word contains an unquoted space, it is invalid. (6.2.4)
873- if (strpos($word, ' ') && $word[0] !== '"')
874- {
875- return false;
876- }
877-
878- if ($this->_validatePhrase(trim($word)) === false) return false;
879- }
880-
881- // Managed to get here, so return the input.
882- return $local_part;
883- }
884-
885- /**
886- * Returns an approximate count of how many addresses are in the
887- * given string. This is APPROXIMATE as it only splits based on a
888- * comma which has no preceding backslash. Could be useful as
889- * large amounts of addresses will end up producing *large*
890- * structures when used with parseAddressList().
891- *
892- * @param string $data Addresses to count
893- * @return int Approximate count
894- */
895- function approximateCount($data)
896- {
897- return count(preg_split('/(?<!\\\\),/', $data));
898- }
899-
900- /**
901- * This is a email validating function separate to the rest of the
902- * class. It simply validates whether an email is of the common
903- * internet form: <user>@<domain>. This can be sufficient for most
904- * people. Optional stricter mode can be utilised which restricts
905- * mailbox characters allowed to alphanumeric, full stop, hyphen
906- * and underscore.
907- *
908- * @param string $data Address to check
909- * @param boolean $strict Optional stricter mode
910- * @return mixed False if it fails, an indexed array
911- * username/domain if it matches
912- */
913- function isValidInetAddress($data, $strict = false)
914- {
915- $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';
916- if (preg_match($regex, trim($data), $matches)) {
917- return array($matches[1], $matches[2]);
918- } else {
919- return false;
920- }
921- }
922-
923-}
1+<?php
2+// +-----------------------------------------------------------------------+
3+// | Copyright (c) 2001-2002, Richard Heyes |
4+// | All rights reserved. |
5+// | |
6+// | Redistribution and use in source and binary forms, with or without |
7+// | modification, are permitted provided that the following conditions |
8+// | are met: |
9+// | |
10+// | o Redistributions of source code must retain the above copyright |
11+// | notice, this list of conditions and the following disclaimer. |
12+// | o Redistributions in binary form must reproduce the above copyright |
13+// | notice, this list of conditions and the following disclaimer in the |
14+// | documentation and/or other materials provided with the distribution.|
15+// | o The names of the authors may not be used to endorse or promote |
16+// | products derived from this software without specific prior written |
17+// | permission. |
18+// | |
19+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30+// | |
31+// +-----------------------------------------------------------------------+
32+// | Authors: Richard Heyes <richard@phpguru.org> |
33+// | Chuck Hagenbuch <chuck@horde.org> |
34+// +-----------------------------------------------------------------------+
35+
36+/**
37+ * RFC 822 Email address list validation Utility
38+ *
39+ * What is it?
40+ *
41+ * This class will take an address string, and parse it into it's consituent
42+ * parts, be that either addresses, groups, or combinations. Nested groups
43+ * are not supported. The structure it returns is pretty straight forward,
44+ * and is similar to that provided by the imap_rfc822_parse_adrlist(). Use
45+ * print_r() to view the structure.
46+ *
47+ * How do I use it?
48+ *
49+ * $address_string = 'My Group: "Richard" <richard@localhost> (A comment), ted@example.com (Ted Bloggs), Barney;';
50+ * $structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true)
51+ * print_r($structure);
52+ *
53+ * @author Richard Heyes <richard@phpguru.org>
54+ * @author Chuck Hagenbuch <chuck@horde.org>
55+ * @version $Revision: 1.7 $
56+ * @license BSD
57+ * @package Mail
58+ */
59+class Mail_RFC822 {
60+
61+ /**
62+ * The address being parsed by the RFC822 object.
63+ * @var string $address
64+ */
65+ var $address = '';
66+
67+ /**
68+ * The default domain to use for unqualified addresses.
69+ * @var string $default_domain
70+ */
71+ var $default_domain = 'localhost';
72+
73+ /**
74+ * Should we return a nested array showing groups, or flatten everything?
75+ * @var boolean $nestGroups
76+ */
77+ var $nestGroups = true;
78+
79+ /**
80+ * Whether or not to validate atoms for non-ascii characters.
81+ * @var boolean $validate
82+ */
83+ var $validate = true;
84+
85+ /**
86+ * The array of raw addresses built up as we parse.
87+ * @var array $addresses
88+ */
89+ var $addresses = array();
90+
91+ /**
92+ * The final array of parsed address information that we build up.
93+ * @var array $structure
94+ */
95+ var $structure = array();
96+
97+ /**
98+ * The current error message, if any.
99+ * @var string $error
100+ */
101+ var $error = null;
102+
103+ /**
104+ * An internal counter/pointer.
105+ * @var integer $index
106+ */
107+ var $index = null;
108+
109+ /**
110+ * The number of groups that have been found in the address list.
111+ * @var integer $num_groups
112+ * @access public
113+ */
114+ var $num_groups = 0;
115+
116+ /**
117+ * A variable so that we can tell whether or not we're inside a
118+ * Mail_RFC822 object.
119+ * @var boolean $mailRFC822
120+ */
121+ var $mailRFC822 = true;
122+
123+ /**
124+ * A limit after which processing stops
125+ * @var int $limit
126+ */
127+ var $limit = null;
128+
129+ /**
130+ * Sets up the object. The address must either be set here or when
131+ * calling parseAddressList(). One or the other.
132+ *
133+ * @access public
134+ * @param string $address The address(es) to validate.
135+ * @param string $default_domain Default domain/host etc. If not supplied, will be set to localhost.
136+ * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
137+ * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
138+ *
139+ * @return object Mail_RFC822 A new Mail_RFC822 object.
140+ */
141+ function Mail_RFC822($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
142+ {
143+ if (isset($address)) $this->address = $address;
144+ if (isset($default_domain)) $this->default_domain = $default_domain;
145+ if (isset($nest_groups)) $this->nestGroups = $nest_groups;
146+ if (isset($validate)) $this->validate = $validate;
147+ if (isset($limit)) $this->limit = $limit;
148+ }
149+
150+ /**
151+ * Starts the whole process. The address must either be set here
152+ * or when creating the object. One or the other.
153+ *
154+ * @access public
155+ * @param string $address The address(es) to validate.
156+ * @param string $default_domain Default domain/host etc.
157+ * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing.
158+ * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance.
159+ *
160+ * @return array A structured array of addresses.
161+ */
162+ function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null)
163+ {
164+ if (!isset($this) || !isset($this->mailRFC822)) {
165+ $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit);
166+ return $obj->parseAddressList();
167+ }
168+
169+ if (isset($address)) $this->address = $address;
170+ if (isset($default_domain)) $this->default_domain = $default_domain;
171+ if (isset($nest_groups)) $this->nestGroups = $nest_groups;
172+ if (isset($validate)) $this->validate = $validate;
173+ if (isset($limit)) $this->limit = $limit;
174+
175+ $this->structure = array();
176+ $this->addresses = array();
177+ $this->error = null;
178+ $this->index = null;
179+
180+ // Unfold any long lines in $this->address.
181+ $this->address = preg_replace('/\r?\n/', "\r\n", $this->address);
182+ $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address);
183+
184+ while ($this->address = $this->_splitAddresses($this->address));
185+
186+ if ($this->address === false || isset($this->error)) {
187+ require_once 'PEAR.php';
188+ return PEAR::raiseError($this->error);
189+ }
190+
191+ // Validate each address individually. If we encounter an invalid
192+ // address, stop iterating and return an error immediately.
193+ foreach ($this->addresses as $address) {
194+ $valid = $this->_validateAddress($address);
195+
196+ if ($valid === false || isset($this->error)) {
197+ require_once 'PEAR.php';
198+ return PEAR::raiseError($this->error);
199+ }
200+
201+ if (!$this->nestGroups) {
202+ $this->structure = array_merge($this->structure, $valid);
203+ } else {
204+ $this->structure[] = $valid;
205+ }
206+ }
207+
208+ return $this->structure;
209+ }
210+
211+ /**
212+ * Splits an address into separate addresses.
213+ *
214+ * @access private
215+ * @param string $address The addresses to split.
216+ * @return boolean Success or failure.
217+ */
218+ function _splitAddresses($address)
219+ {
220+ if (!empty($this->limit) && count($this->addresses) == $this->limit) {
221+ return '';
222+ }
223+
224+ if ($this->_isGroup($address) && !isset($this->error)) {
225+ $split_char = ';';
226+ $is_group = true;
227+ } elseif (!isset($this->error)) {
228+ $split_char = ',';
229+ $is_group = false;
230+ } elseif (isset($this->error)) {
231+ return false;
232+ }
233+
234+ // Split the string based on the above ten or so lines.
235+ $parts = explode($split_char, $address);
236+ $string = $this->_splitCheck($parts, $split_char);
237+
238+ // If a group...
239+ if ($is_group) {
240+ // If $string does not contain a colon outside of
241+ // brackets/quotes etc then something's fubar.
242+
243+ // First check there's a colon at all:
244+ if (strpos($string, ':') === false) {
245+ $this->error = 'Invalid address: ' . $string;
246+ return false;
247+ }
248+
249+ // Now check it's outside of brackets/quotes:
250+ if (!$this->_splitCheck(explode(':', $string), ':')) {
251+ return false;
252+ }
253+
254+ // We must have a group at this point, so increase the counter:
255+ $this->num_groups++;
256+ }
257+
258+ // $string now contains the first full address/group.
259+ // Add to the addresses array.
260+ $this->addresses[] = array(
261+ 'address' => trim($string),
262+ 'group' => $is_group
263+ );
264+
265+ // Remove the now stored address from the initial line, the +1
266+ // is to account for the explode character.
267+ $address = trim(substr($address, strlen($string) + 1));
268+
269+ // If the next char is a comma and this was a group, then
270+ // there are more addresses, otherwise, if there are any more
271+ // chars, then there is another address.
272+ if ($is_group && substr($address, 0, 1) == ','){
273+ $address = trim(substr($address, 1));
274+ return $address;
275+
276+ } elseif (strlen($address) > 0) {
277+ return $address;
278+
279+ } else {
280+ return '';
281+ }
282+
283+ // If you got here then something's off
284+ return false;
285+ }
286+
287+ /**
288+ * Checks for a group at the start of the string.
289+ *
290+ * @access private
291+ * @param string $address The address to check.
292+ * @return boolean Whether or not there is a group at the start of the string.
293+ */
294+ function _isGroup($address)
295+ {
296+ // First comma not in quotes, angles or escaped:
297+ $parts = explode(',', $address);
298+ $string = $this->_splitCheck($parts, ',');
299+
300+ // Now we have the first address, we can reliably check for a
301+ // group by searching for a colon that's not escaped or in
302+ // quotes or angle brackets.
303+ if (count($parts = explode(':', $string)) > 1) {
304+ $string2 = $this->_splitCheck($parts, ':');
305+ return ($string2 !== $string);
306+ } else {
307+ return false;
308+ }
309+ }
310+
311+ /**
312+ * A common function that will check an exploded string.
313+ *
314+ * @access private
315+ * @param array $parts The exloded string.
316+ * @param string $char The char that was exploded on.
317+ * @return mixed False if the string contains unclosed quotes/brackets, or the string on success.
318+ */
319+ function _splitCheck($parts, $char)
320+ {
321+ $string = $parts[0];
322+
323+ for ($i = 0; $i < count($parts); $i++) {
324+ if ($this->_hasUnclosedQuotes($string)
325+ || $this->_hasUnclosedBrackets($string, '<>')
326+ || $this->_hasUnclosedBrackets($string, '[]')
327+ || $this->_hasUnclosedBrackets($string, '()')
328+ || substr($string, -1) == '\\') {
329+ if (isset($parts[$i + 1])) {
330+ $string = $string . $char . $parts[$i + 1];
331+ } else {
332+ $this->error = 'Invalid address spec. Unclosed bracket or quotes';
333+ return false;
334+ }
335+ } else {
336+ $this->index = $i;
337+ break;
338+ }
339+ }
340+
341+ return $string;
342+ }
343+
344+ /**
345+ * Checks if a string has an unclosed quotes or not.
346+ *
347+ * @access private
348+ * @param string $string The string to check.
349+ * @return boolean True if there are unclosed quotes inside the string, false otherwise.
350+ */
351+ function _hasUnclosedQuotes($string)
352+ {
353+ $string = explode('"', $string);
354+ $string_cnt = count($string);
355+
356+ for ($i = 0; $i < (count($string) - 1); $i++)
357+ if (substr($string[$i], -1) == '\\')
358+ $string_cnt--;
359+
360+ return ($string_cnt % 2 === 0);
361+ }
362+
363+ /**
364+ * Checks if a string has an unclosed brackets or not. IMPORTANT:
365+ * This function handles both angle brackets and square brackets;
366+ *
367+ * @access private
368+ * @param string $string The string to check.
369+ * @param string $chars The characters to check for.
370+ * @return boolean True if there are unclosed brackets inside the string, false otherwise.
371+ */
372+ function _hasUnclosedBrackets($string, $chars)
373+ {
374+ $num_angle_start = substr_count($string, $chars[0]);
375+ $num_angle_end = substr_count($string, $chars[1]);
376+
377+ $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]);
378+ $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]);
379+
380+ if ($num_angle_start < $num_angle_end) {
381+ $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')';
382+ return false;
383+ } else {
384+ return ($num_angle_start > $num_angle_end);
385+ }
386+ }
387+
388+ /**
389+ * Sub function that is used only by hasUnclosedBrackets().
390+ *
391+ * @access private
392+ * @param string $string The string to check.
393+ * @param integer &$num The number of occurences.
394+ * @param string $char The character to count.
395+ * @return integer The number of occurences of $char in $string, adjusted for backslashes.
396+ */
397+ function _hasUnclosedBracketsSub($string, &$num, $char)
398+ {
399+ $parts = explode($char, $string);
400+ for ($i = 0; $i < count($parts); $i++){
401+ if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i]))
402+ $num--;
403+ if (isset($parts[$i + 1]))
404+ $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1];
405+ }
406+
407+ return $num;
408+ }
409+
410+ /**
411+ * Function to begin checking the address.
412+ *
413+ * @access private
414+ * @param string $address The address to validate.
415+ * @return mixed False on failure, or a structured array of address information on success.
416+ */
417+ function _validateAddress($address)
418+ {
419+ $is_group = false;
420+ $addresses = array();
421+
422+ if ($address['group']) {
423+ $is_group = true;
424+
425+ // Get the group part of the name
426+ $parts = explode(':', $address['address']);
427+ $groupname = $this->_splitCheck($parts, ':');
428+ $structure = array();
429+
430+ // And validate the group part of the name.
431+ if (!$this->_validatePhrase($groupname)){
432+ $this->error = 'Group name did not validate.';
433+ return false;
434+ } else {
435+ // Don't include groups if we are not nesting
436+ // them. This avoids returning invalid addresses.
437+ if ($this->nestGroups) {
438+ $structure = new stdClass;
439+ $structure->groupname = $groupname;
440+ }
441+ }
442+
443+ $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':')));
444+ }
445+
446+ // If a group then split on comma and put into an array.
447+ // Otherwise, Just put the whole address in an array.
448+ if ($is_group) {
449+ while (strlen($address['address']) > 0) {
450+ $parts = explode(',', $address['address']);
451+ $addresses[] = $this->_splitCheck($parts, ',');
452+ $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ',')));
453+ }
454+ } else {
455+ $addresses[] = $address['address'];
456+ }
457+
458+ // Check that $addresses is set, if address like this:
459+ // Groupname:;
460+ // Then errors were appearing.
461+ if (!count($addresses)){
462+ $this->error = 'Empty group.';
463+ return false;
464+ }
465+
466+ // Trim the whitespace from all of the address strings.
467+ array_map('trim', $addresses);
468+
469+ // Validate each mailbox.
470+ // Format could be one of: name <geezer@domain.com>
471+ // geezer@domain.com
472+ // geezer
473+ // ... or any other format valid by RFC 822.
474+ for ($i = 0; $i < count($addresses); $i++) {
475+ if (!$this->validateMailbox($addresses[$i])) {
476+ if (empty($this->error)) {
477+ $this->error = 'Validation failed for: ' . $addresses[$i];
478+ }
479+ return false;
480+ }
481+ }
482+
483+ // Nested format
484+ if ($this->nestGroups) {
485+ if ($is_group) {
486+ $structure->addresses = $addresses;
487+ } else {
488+ $structure = $addresses[0];
489+ }
490+
491+ // Flat format
492+ } else {
493+ if ($is_group) {
494+ $structure = array_merge($structure, $addresses);
495+ } else {
496+ $structure = $addresses;
497+ }
498+ }
499+
500+ return $structure;
501+ }
502+
503+ /**
504+ * Function to validate a phrase.
505+ *
506+ * @access private
507+ * @param string $phrase The phrase to check.
508+ * @return boolean Success or failure.
509+ */
510+ function _validatePhrase($phrase)
511+ {
512+ // Splits on one or more Tab or space.
513+ $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY);
514+
515+ $phrase_parts = array();
516+ while (count($parts) > 0){
517+ $phrase_parts[] = $this->_splitCheck($parts, ' ');
518+ for ($i = 0; $i < $this->index + 1; $i++)
519+ array_shift($parts);
520+ }
521+
522+ foreach ($phrase_parts as $part) {
523+ // If quoted string:
524+ if (substr($part, 0, 1) == '"') {
525+ if (!$this->_validateQuotedString($part)) {
526+ return false;
527+ }
528+ continue;
529+ }
530+
531+ // Otherwise it's an atom:
532+ if (!$this->_validateAtom($part)) return false;
533+ }
534+
535+ return true;
536+ }
537+
538+ /**
539+ * Function to validate an atom which from rfc822 is:
540+ * atom = 1*<any CHAR except specials, SPACE and CTLs>
541+ *
542+ * If validation ($this->validate) has been turned off, then
543+ * validateAtom() doesn't actually check anything. This is so that you
544+ * can split a list of addresses up before encoding personal names
545+ * (umlauts, etc.), for example.
546+ *
547+ * @access private
548+ * @param string $atom The string to check.
549+ * @return boolean Success or failure.
550+ */
551+ function _validateAtom($atom)
552+ {
553+ if (!$this->validate) {
554+ // Validation has been turned off; assume the atom is okay.
555+ return true;
556+ }
557+
558+ // Check for any char from ASCII 0 - ASCII 127
559+ if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) {
560+ return false;
561+ }
562+
563+ // Check for specials:
564+ if (preg_match('/[][()<>@,;\\:". ]/', $atom)) {
565+ return false;
566+ }
567+
568+ // Check for control characters (ASCII 0-31):
569+ if (preg_match('/[\\x00-\\x1F]+/', $atom)) {
570+ return false;
571+ }
572+
573+ return true;
574+ }
575+
576+ /**
577+ * Function to validate quoted string, which is:
578+ * quoted-string = <"> *(qtext/quoted-pair) <">
579+ *
580+ * @access private
581+ * @param string $qstring The string to check
582+ * @return boolean Success or failure.
583+ */
584+ function _validateQuotedString($qstring)
585+ {
586+ // Leading and trailing "
587+ $qstring = substr($qstring, 1, -1);
588+
589+ // Perform check, removing quoted characters first.
590+ return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring));
591+ }
592+
593+ /**
594+ * Function to validate a mailbox, which is:
595+ * mailbox = addr-spec ; simple address
596+ * / phrase route-addr ; name and route-addr
597+ *
598+ * @access public
599+ * @param string &$mailbox The string to check.
600+ * @return boolean Success or failure.
601+ */
602+ function validateMailbox(&$mailbox)
603+ {
604+ // A couple of defaults.
605+ $phrase = '';
606+ $comment = '';
607+ $comments = array();
608+
609+ // Catch any RFC822 comments and store them separately.
610+ $_mailbox = $mailbox;
611+ while (strlen(trim($_mailbox)) > 0) {
612+ $parts = explode('(', $_mailbox);
613+ $before_comment = $this->_splitCheck($parts, '(');
614+ if ($before_comment != $_mailbox) {
615+ // First char should be a (.
616+ $comment = substr(str_replace($before_comment, '', $_mailbox), 1);
617+ $parts = explode(')', $comment);
618+ $comment = $this->_splitCheck($parts, ')');
619+ $comments[] = $comment;
620+
621+ // +1 is for the trailing )
622+ $_mailbox = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);
623+ } else {
624+ break;
625+ }
626+ }
627+
628+ foreach ($comments as $comment) {
629+ $mailbox = str_replace("($comment)", '', $mailbox);
630+ }
631+
632+ $mailbox = trim($mailbox);
633+
634+ // Check for name + route-addr
635+ if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') {
636+ $parts = explode('<', $mailbox);
637+ $name = $this->_splitCheck($parts, '<');
638+
639+ $phrase = trim($name);
640+ $route_addr = trim(substr($mailbox, strlen($name.'<'), -1));
641+
642+ if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) {
643+ return false;
644+ }
645+
646+ // Only got addr-spec
647+ } else {
648+ // First snip angle brackets if present.
649+ if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') {
650+ $addr_spec = substr($mailbox, 1, -1);
651+ } else {
652+ $addr_spec = $mailbox;
653+ }
654+
655+ if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
656+ return false;
657+ }
658+ }
659+
660+ // Construct the object that will be returned.
661+ $mbox = new stdClass();
662+
663+ // Add the phrase (even if empty) and comments
664+ $mbox->personal = $phrase;
665+ $mbox->comment = isset($comments) ? $comments : array();
666+
667+ if (isset($route_addr)) {
668+ $mbox->mailbox = $route_addr['local_part'];
669+ $mbox->host = $route_addr['domain'];
670+ $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : '';
671+ } else {
672+ $mbox->mailbox = $addr_spec['local_part'];
673+ $mbox->host = $addr_spec['domain'];
674+ }
675+
676+ $mailbox = $mbox;
677+ return true;
678+ }
679+
680+ /**
681+ * This function validates a route-addr which is:
682+ * route-addr = "<" [route] addr-spec ">"
683+ *
684+ * Angle brackets have already been removed at the point of
685+ * getting to this function.
686+ *
687+ * @access private
688+ * @param string $route_addr The string to check.
689+ * @return mixed False on failure, or an array containing validated address/route information on success.
690+ */
691+ function _validateRouteAddr($route_addr)
692+ {
693+ // Check for colon.
694+ if (strpos($route_addr, ':') !== false) {
695+ $parts = explode(':', $route_addr);
696+ $route = $this->_splitCheck($parts, ':');
697+ } else {
698+ $route = $route_addr;
699+ }
700+
701+ // If $route is same as $route_addr then the colon was in
702+ // quotes or brackets or, of course, non existent.
703+ if ($route === $route_addr){
704+ unset($route);
705+ $addr_spec = $route_addr;
706+ if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
707+ return false;
708+ }
709+ } else {
710+ // Validate route part.
711+ if (($route = $this->_validateRoute($route)) === false) {
712+ return false;
713+ }
714+
715+ $addr_spec = substr($route_addr, strlen($route . ':'));
716+
717+ // Validate addr-spec part.
718+ if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) {
719+ return false;
720+ }
721+ }
722+
723+ if (isset($route)) {
724+ $return['adl'] = $route;
725+ } else {
726+ $return['adl'] = '';
727+ }
728+
729+ $return = array_merge($return, $addr_spec);
730+ return $return;
731+ }
732+
733+ /**
734+ * Function to validate a route, which is:
735+ * route = 1#("@" domain) ":"
736+ *
737+ * @access private
738+ * @param string $route The string to check.
739+ * @return mixed False on failure, or the validated $route on success.
740+ */
741+ function _validateRoute($route)
742+ {
743+ // Split on comma.
744+ $domains = explode(',', trim($route));
745+
746+ foreach ($domains as $domain) {
747+ $domain = str_replace('@', '', trim($domain));
748+ if (!$this->_validateDomain($domain)) return false;
749+ }
750+
751+ return $route;
752+ }
753+
754+ /**
755+ * Function to validate a domain, though this is not quite what
756+ * you expect of a strict internet domain.
757+ *
758+ * domain = sub-domain *("." sub-domain)
759+ *
760+ * @access private
761+ * @param string $domain The string to check.
762+ * @return mixed False on failure, or the validated domain on success.
763+ */
764+ function _validateDomain($domain)
765+ {
766+ // Note the different use of $subdomains and $sub_domains
767+ $subdomains = explode('.', $domain);
768+
769+ while (count($subdomains) > 0) {
770+ $sub_domains[] = $this->_splitCheck($subdomains, '.');
771+ for ($i = 0; $i < $this->index + 1; $i++)
772+ array_shift($subdomains);
773+ }
774+
775+ foreach ($sub_domains as $sub_domain) {
776+ if (!$this->_validateSubdomain(trim($sub_domain)))
777+ return false;
778+ }
779+
780+ // Managed to get here, so return input.
781+ return $domain;
782+ }
783+
784+ /**
785+ * Function to validate a subdomain:
786+ * subdomain = domain-ref / domain-literal
787+ *
788+ * @access private
789+ * @param string $subdomain The string to check.
790+ * @return boolean Success or failure.
791+ */
792+ function _validateSubdomain($subdomain)
793+ {
794+ if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){
795+ if (!$this->_validateDliteral($arr[1])) return false;
796+ } else {
797+ if (!$this->_validateAtom($subdomain)) return false;
798+ }
799+
800+ // Got here, so return successful.
801+ return true;
802+ }
803+
804+ /**
805+ * Function to validate a domain literal:
806+ * domain-literal = "[" *(dtext / quoted-pair) "]"
807+ *
808+ * @access private
809+ * @param string $dliteral The string to check.
810+ * @return boolean Success or failure.
811+ */
812+ function _validateDliteral($dliteral)
813+ {
814+ return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\';
815+ }
816+
817+ /**
818+ * Function to validate an addr-spec.
819+ *
820+ * addr-spec = local-part "@" domain
821+ *
822+ * @access private
823+ * @param string $addr_spec The string to check.
824+ * @return mixed False on failure, or the validated addr-spec on success.
825+ */
826+ function _validateAddrSpec($addr_spec)
827+ {
828+ $addr_spec = trim($addr_spec);
829+
830+ // Split on @ sign if there is one.
831+ if (strpos($addr_spec, '@') !== false) {
832+ $parts = explode('@', $addr_spec);
833+ $local_part = $this->_splitCheck($parts, '@');
834+ $domain = substr($addr_spec, strlen($local_part . '@'));
835+
836+ // No @ sign so assume the default domain.
837+ } else {
838+ $local_part = $addr_spec;
839+ $domain = $this->default_domain;
840+ }
841+
842+ if (($local_part = $this->_validateLocalPart($local_part)) === false) return false;
843+ if (($domain = $this->_validateDomain($domain)) === false) return false;
844+
845+ // Got here so return successful.
846+ return array('local_part' => $local_part, 'domain' => $domain);
847+ }
848+
849+ /**
850+ * Function to validate the local part of an address:
851+ * local-part = word *("." word)
852+ *
853+ * @access private
854+ * @param string $local_part
855+ * @return mixed False on failure, or the validated local part on success.
856+ */
857+ function _validateLocalPart($local_part)
858+ {
859+ $parts = explode('.', $local_part);
860+ $words = array();
861+
862+ // Split the local_part into words.
863+ while (count($parts) > 0){
864+ $words[] = $this->_splitCheck($parts, '.');
865+ for ($i = 0; $i < $this->index + 1; $i++) {
866+ array_shift($parts);
867+ }
868+ }
869+
870+ // Validate each word.
871+ foreach ($words as $word) {
872+ // If this word contains an unquoted space, it is invalid. (6.2.4)
873+ if (strpos($word, ' ') && $word[0] !== '"')
874+ {
875+ return false;
876+ }
877+
878+ if ($this->_validatePhrase(trim($word)) === false) return false;
879+ }
880+
881+ // Managed to get here, so return the input.
882+ return $local_part;
883+ }
884+
885+ /**
886+ * Returns an approximate count of how many addresses are in the
887+ * given string. This is APPROXIMATE as it only splits based on a
888+ * comma which has no preceding backslash. Could be useful as
889+ * large amounts of addresses will end up producing *large*
890+ * structures when used with parseAddressList().
891+ *
892+ * @param string $data Addresses to count
893+ * @return int Approximate count
894+ */
895+ function approximateCount($data)
896+ {
897+ return count(preg_split('/(?<!\\\\),/', $data));
898+ }
899+
900+ /**
901+ * This is a email validating function separate to the rest of the
902+ * class. It simply validates whether an email is of the common
903+ * internet form: <user>@<domain>. This can be sufficient for most
904+ * people. Optional stricter mode can be utilised which restricts
905+ * mailbox characters allowed to alphanumeric, full stop, hyphen
906+ * and underscore.
907+ *
908+ * @param string $data Address to check
909+ * @param boolean $strict Optional stricter mode
910+ * @return mixed False if it fails, an indexed array
911+ * username/domain if it matches
912+ */
913+ function isValidInetAddress($data, $strict = false)
914+ {
915+ $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';
916+ if (preg_match($regex, trim($data), $matches)) {
917+ return array($matches[1], $matches[2]);
918+ } else {
919+ return false;
920+ }
921+ }
922+
923+}
--- a/trunk/NP_Moblog/sharedlibs/Mail/mimeDecode.php
+++ b/trunk/NP_Moblog/sharedlibs/Mail/mimeDecode.php
@@ -1,837 +1,849 @@
1-<?php
2-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3-// +-----------------------------------------------------------------------+
4-// | Copyright (c) 2002-2003 Richard Heyes |
5-// | Copyright (c) 2003-2005 The PHP Group |
6-// | All rights reserved. |
7-// | |
8-// | Redistribution and use in source and binary forms, with or without |
9-// | modification, are permitted provided that the following conditions |
10-// | are met: |
11-// | |
12-// | o Redistributions of source code must retain the above copyright |
13-// | notice, this list of conditions and the following disclaimer. |
14-// | o Redistributions in binary form must reproduce the above copyright |
15-// | notice, this list of conditions and the following disclaimer in the |
16-// | documentation and/or other materials provided with the distribution.|
17-// | o The names of the authors may not be used to endorse or promote |
18-// | products derived from this software without specific prior written |
19-// | permission. |
20-// | |
21-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32-// | |
33-// +-----------------------------------------------------------------------+
34-// | Author: Richard Heyes <richard@phpguru.org> |
35-// +-----------------------------------------------------------------------+
36-
37-require_once 'PEAR.php';
38-
39-/**
40-* +----------------------------- IMPORTANT ------------------------------+
41-* | Usage of this class compared to native php extensions such as |
42-* | mailparse or imap, is slow and may be feature deficient. If available|
43-* | you are STRONGLY recommended to use the php extensions. |
44-* +----------------------------------------------------------------------+
45-*
46-* Mime Decoding class
47-*
48-* This class will parse a raw mime email and return
49-* the structure. Returned structure is similar to
50-* that returned by imap_fetchstructure().
51-*
52-* USAGE: (assume $input is your raw email)
53-*
54-* $decode = new Mail_mimeDecode($input, "\r\n");
55-* $structure = $decode->decode();
56-* print_r($structure);
57-*
58-* Or statically:
59-*
60-* $params['input'] = $input;
61-* $structure = Mail_mimeDecode::decode($params);
62-* print_r($structure);
63-*
64-* TODO:
65-* o Implement multipart/appledouble
66-* o UTF8: ???
67-
68- > 4. We have also found a solution for decoding the UTF-8
69- > headers. Therefore I made the following function:
70- >
71- > function decode_utf8($txt) {
72- > $trans=array("ナ&#8216;"=>"テオ","ナア"=>"テサ","ナ?=>"テ&#8226;","ナー"
73- =>"テ&#8250;");
74- > $txt=strtr($txt,$trans);
75- > return(utf8_decode($txt));
76- > }
77- >
78- > And I have inserted the following line to the class:
79- >
80- > if (strtolower($charset)=="utf-8") $text=decode_utf8($text);
81- >
82- > ... before the following one in the "_decodeHeader" function:
83- >
84- > $input = str_replace($encoded, $text, $input);
85- >
86- > This way from now on it can easily decode the UTF-8 headers too.
87-
88-*
89-* @author Richard Heyes <richard@phpguru.org>
90-* @version $Revision: 1.1 $
91-* @package Mail
92-*/
93-class Mail_mimeDecode extends PEAR
94-{
95- /**
96- * The raw email to decode
97- * @var string
98- */
99- var $_input;
100-
101- /**
102- * The header part of the input
103- * @var string
104- */
105- var $_header;
106-
107- /**
108- * The body part of the input
109- * @var string
110- */
111- var $_body;
112-
113- /**
114- * If an error occurs, this is used to store the message
115- * @var string
116- */
117- var $_error;
118-
119- /**
120- * Flag to determine whether to include bodies in the
121- * returned object.
122- * @var boolean
123- */
124- var $_include_bodies;
125-
126- /**
127- * Flag to determine whether to decode bodies
128- * @var boolean
129- */
130- var $_decode_bodies;
131-
132- /**
133- * Flag to determine whether to decode headers
134- * @var boolean
135- */
136- var $_decode_headers;
137-
138- /**
139- * Constructor.
140- *
141- * Sets up the object, initialise the variables, and splits and
142- * stores the header and body of the input.
143- *
144- * @param string The input to decode
145- * @access public
146- */
147- function Mail_mimeDecode($input)
148- {
149- list($header, $body) = $this->_splitBodyHeader($input);
150-
151- $this->_input = $input;
152- $this->_header = $header;
153- $this->_body = $body;
154- $this->_decode_bodies = false;
155- $this->_include_bodies = true;
156- }
157-
158- /**
159- * Begins the decoding process. If called statically
160- * it will create an object and call the decode() method
161- * of it.
162- *
163- * @param array An array of various parameters that determine
164- * various things:
165- * include_bodies - Whether to include the body in the returned
166- * object.
167- * decode_bodies - Whether to decode the bodies
168- * of the parts. (Transfer encoding)
169- * decode_headers - Whether to decode headers
170- * input - If called statically, this will be treated
171- * as the input
172- * @return object Decoded results
173- * @access public
174- */
175- function decode($params = null)
176- {
177- // determine if this method has been called statically
178- $isStatic = !(isset($this) && get_class($this) == __CLASS__);
179-
180- // Have we been called statically?
181- // If so, create an object and pass details to that.
182- if ($isStatic AND isset($params['input'])) {
183-
184- $obj = new Mail_mimeDecode($params['input']);
185- $structure = $obj->decode($params);
186-
187- // Called statically but no input
188- } elseif ($isStatic) {
189- return PEAR::raiseError('Called statically and no input given');
190-
191- // Called via an object
192- } else {
193- $this->_include_bodies = isset($params['include_bodies']) ?
194- $params['include_bodies'] : false;
195- $this->_decode_bodies = isset($params['decode_bodies']) ?
196- $params['decode_bodies'] : false;
197- $this->_decode_headers = isset($params['decode_headers']) ?
198- $params['decode_headers'] : false;
199-
200- $structure = $this->_decode($this->_header, $this->_body);
201- if ($structure === false) {
202- $structure = $this->raiseError($this->_error);
203- }
204- }
205-
206- return $structure;
207- }
208-
209- /**
210- * Performs the decoding. Decodes the body string passed to it
211- * If it finds certain content-types it will call itself in a
212- * recursive fashion
213- *
214- * @param string Header section
215- * @param string Body section
216- * @return object Results of decoding process
217- * @access private
218- */
219- function _decode($headers, $body, $default_ctype = 'text/plain')
220- {
221- $return = new stdClass;
222- $return->headers = array();
223- $headers = $this->_parseHeaders($headers);
224-
225- foreach ($headers as $value) {
226- if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
227- $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
228- $return->headers[strtolower($value['name'])][] = $value['value'];
229-
230- } elseif (isset($return->headers[strtolower($value['name'])])) {
231- $return->headers[strtolower($value['name'])][] = $value['value'];
232-
233- } else {
234- $return->headers[strtolower($value['name'])] = $value['value'];
235- }
236- }
237-
238- reset($headers);
239- while (list($key, $value) = each($headers)) {
240- $headers[$key]['name'] = strtolower($headers[$key]['name']);
241- switch ($headers[$key]['name']) {
242-
243- case 'content-type':
244- $content_type = $this->_parseHeaderValue($headers[$key]['value']);
245-
246- if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
247- $return->ctype_primary = $regs[1];
248- $return->ctype_secondary = $regs[2];
249- }
250-
251- if (isset($content_type['other'])) {
252- while (list($p_name, $p_value) = each($content_type['other'])) {
253- $return->ctype_parameters[$p_name] = $p_value;
254- }
255- }
256- break;
257-
258- case 'content-disposition':
259- $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
260- $return->disposition = $content_disposition['value'];
261- if (isset($content_disposition['other'])) {
262- while (list($p_name, $p_value) = each($content_disposition['other'])) {
263- $return->d_parameters[$p_name] = $p_value;
264- }
265- }
266- break;
267-
268- case 'content-transfer-encoding':
269- $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
270- break;
271- }
272- }
273-
274- if (isset($content_type)) {
275- switch (strtolower($content_type['value'])) {
276- case 'text/plain':
277- $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
278- $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
279- break;
280-
281- case 'text/html':
282- $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
283- $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
284- break;
285-
286- case 'multipart/parallel':
287- case 'multipart/report': // RFC1892
288- case 'multipart/signed': // PGP
289- case 'multipart/digest':
290- case 'multipart/alternative':
291- case 'multipart/related':
292- case 'multipart/mixed':
293- if(!isset($content_type['other']['boundary'])){
294- $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
295- return false;
296- }
297-
298- $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
299-
300- $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
301- for ($i = 0; $i < count($parts); $i++) {
302- list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
303- $part = $this->_decode($part_header, $part_body, $default_ctype);
304- if($part === false)
305- $part = $this->raiseError($this->_error);
306- $return->parts[] = $part;
307- }
308- break;
309-
310- case 'message/rfc822':
311- $obj = &new Mail_mimeDecode($body);
312- $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
313- 'decode_bodies' => $this->_decode_bodies,
314- 'decode_headers' => $this->_decode_headers));
315- unset($obj);
316- break;
317-
318- default:
319- if(!isset($content_transfer_encoding['value']))
320- $content_transfer_encoding['value'] = '7bit';
321- $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
322- break;
323- }
324-
325- } else {
326- $ctype = explode('/', $default_ctype);
327- $return->ctype_primary = $ctype[0];
328- $return->ctype_secondary = $ctype[1];
329- $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
330- }
331-
332- return $return;
333- }
334-
335- /**
336- * Given the output of the above function, this will return an
337- * array of references to the parts, indexed by mime number.
338- *
339- * @param object $structure The structure to go through
340- * @param string $mime_number Internal use only.
341- * @return array Mime numbers
342- */
343- function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
344- {
345- $return = array();
346- if (!empty($structure->parts)) {
347- if ($mime_number != '') {
348- $structure->mime_id = $prepend . $mime_number;
349- $return[$prepend . $mime_number] = &$structure;
350- }
351- for ($i = 0; $i < count($structure->parts); $i++) {
352-
353-
354- if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
355- $prepend = $prepend . $mime_number . '.';
356- $_mime_number = '';
357- } else {
358- $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
359- }
360-
361- $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
362- foreach ($arr as $key => $val) {
363- $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
364- }
365- }
366- } else {
367- if ($mime_number == '') {
368- $mime_number = '1';
369- }
370- $structure->mime_id = $prepend . $mime_number;
371- $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
372- }
373-
374- return $return;
375- }
376-
377- /**
378- * Given a string containing a header and body
379- * section, this function will split them (at the first
380- * blank line) and return them.
381- *
382- * @param string Input to split apart
383- * @return array Contains header and body section
384- * @access private
385- */
386- function _splitBodyHeader($input)
387- {
388- if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
389- return array($match[1], $match[2]);
390- }
391- $this->_error = 'Could not split header and body';
392- return false;
393- }
394-
395- /**
396- * Parse headers given in $input and return
397- * as assoc array.
398- *
399- * @param string Headers to parse
400- * @return array Contains parsed headers
401- * @access private
402- */
403- function _parseHeaders($input)
404- {
405-
406- if ($input !== '') {
407- // Unfold the input
408- $input = preg_replace("/\r?\n/", "\r\n", $input);
409- $input = preg_replace("/\r\n(\t| )+/", ' ', $input);
410- $headers = explode("\r\n", trim($input));
411-
412- foreach ($headers as $value) {
413- $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
414- $hdr_value = substr($value, $pos+1);
415- if($hdr_value[0] == ' ')
416- $hdr_value = substr($hdr_value, 1);
417-
418- $return[] = array(
419- 'name' => $hdr_name,
420- 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
421- );
422- }
423- } else {
424- $return = array();
425- }
426-
427- return $return;
428- }
429-
430- /**
431- * Function to parse a header value,
432- * extract first part, and any secondary
433- * parts (after ;) This function is not as
434- * robust as it could be. Eg. header comments
435- * in the wrong place will probably break it.
436- *
437- * @param string Header value to parse
438- * @return array Contains parsed result
439- * @access private
440- */
441- function _parseHeaderValue($input)
442- {
443-
444- if (($pos = strpos($input, ';')) !== false) {
445-
446- $return['value'] = trim(substr($input, 0, $pos));
447- $input = trim(substr($input, $pos+1));
448-
449- if (strlen($input) > 0) {
450-
451- // This splits on a semi-colon, if there's no preceeding backslash
452- // Now works with quoted values; had to glue the \; breaks in PHP
453- // the regex is already bordering on incomprehensible
454- $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
455- preg_match_all($splitRegex, $input, $matches);
456- $parameters = array();
457- for ($i=0; $i<count($matches[0]); $i++) {
458- $param = $matches[0][$i];
459- while (substr($param, -2) == '\;') {
460- $param .= $matches[0][++$i];
461- }
462- $parameters[] = $param;
463- }
464-
465- for ($i = 0; $i < count($parameters); $i++) {
466- $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
467- $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
468- if ($param_value[0] == '"') {
469- $param_value = substr($param_value, 1, -1);
470- }
471- $return['other'][$param_name] = $param_value;
472- $return['other'][strtolower($param_name)] = $param_value;
473- }
474- }
475- } else {
476- $return['value'] = trim($input);
477- }
478-
479- return $return;
480- }
481-
482- /**
483- * This function splits the input based
484- * on the given boundary
485- *
486- * @param string Input to parse
487- * @return array Contains array of resulting mime parts
488- * @access private
489- */
490- function _boundarySplit($input, $boundary)
491- {
492- $parts = array();
493-
494- $bs_possible = substr($boundary, 2, -2);
495- $bs_check = '\"' . $bs_possible . '\"';
496-
497- if ($boundary == $bs_check) {
498- $boundary = $bs_possible;
499- }
500-
501- $tmp = explode('--' . $boundary, $input);
502-
503- for ($i = 1; $i < count($tmp) - 1; $i++) {
504- $parts[] = $tmp[$i];
505- }
506-
507- return $parts;
508- }
509-
510- /**
511- * Given a header, this function will decode it
512- * according to RFC2047. Probably not *exactly*
513- * conformant, but it does pass all the given
514- * examples (in RFC2047).
515- *
516- * @param string Input header value to decode
517- * @return string Decoded header value
518- * @access private
519- */
520- function _decodeHeader($input)
521- {
522- // Remove white space between encoded-words
523- $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
524-
525- // For each encoded-word...
526- while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
527-
528- $encoded = $matches[1];
529- $charset = $matches[2];
530- $encoding = $matches[3];
531- $text = $matches[4];
532-
533- switch (strtolower($encoding)) {
534- case 'b':
535- $text = base64_decode($text);
536- break;
537-
538- case 'q':
539- $text = str_replace('_', ' ', $text);
540- preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
541- foreach($matches[1] as $value)
542- $text = str_replace('='.$value, chr(hexdec($value)), $text);
543- break;
544- }
545-
546- $input = str_replace($encoded, $text, $input);
547- }
548-
549- return $input;
550- }
551-
552- /**
553- * Given a body string and an encoding type,
554- * this function will decode and return it.
555- *
556- * @param string Input body to decode
557- * @param string Encoding type to use.
558- * @return string Decoded body
559- * @access private
560- */
561- function _decodeBody($input, $encoding = '7bit')
562- {
563- switch (strtolower($encoding)) {
564- case '7bit':
565- return $input;
566- break;
567-
568- case 'quoted-printable':
569- return $this->_quotedPrintableDecode($input);
570- break;
571-
572- case 'base64':
573- return base64_decode($input);
574- break;
575-
576- default:
577- return $input;
578- }
579- }
580-
581- /**
582- * Given a quoted-printable string, this
583- * function will decode and return it.
584- *
585- * @param string Input body to decode
586- * @return string Decoded body
587- * @access private
588- */
589- function _quotedPrintableDecode($input)
590- {
591- // Remove soft line breaks
592- $input = preg_replace("/=\r?\n/", '', $input);
593-
594- // Replace encoded characters
595- $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
596-
597- return $input;
598- }
599-
600- /**
601- * Checks the input for uuencoded files and returns
602- * an array of them. Can be called statically, eg:
603- *
604- * $files =& Mail_mimeDecode::uudecode($some_text);
605- *
606- * It will check for the begin 666 ... end syntax
607- * however and won't just blindly decode whatever you
608- * pass it.
609- *
610- * @param string Input body to look for attahcments in
611- * @return array Decoded bodies, filenames and permissions
612- * @access public
613- * @author Unknown
614- */
615- function &uudecode($input)
616- {
617- // Find all uuencoded sections
618- preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
619-
620- for ($j = 0; $j < count($matches[3]); $j++) {
621-
622- $str = $matches[3][$j];
623- $filename = $matches[2][$j];
624- $fileperm = $matches[1][$j];
625-
626- $file = '';
627- $str = preg_split("/\r?\n/", trim($str));
628- $strlen = count($str);
629-
630- for ($i = 0; $i < $strlen; $i++) {
631- $pos = 1;
632- $d = 0;
633- $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
634-
635- while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
636- $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
637- $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
638- $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
639- $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
640- $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
641-
642- $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
643-
644- $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077));
645-
646- $pos += 4;
647- $d += 3;
648- }
649-
650- if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
651- $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
652- $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
653- $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
654- $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
655-
656- $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
657-
658- $pos += 3;
659- $d += 2;
660- }
661-
662- if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
663- $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
664- $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
665- $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
666-
667- }
668- }
669- $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
670- }
671-
672- return $files;
673- }
674-
675- /**
676- * getSendArray() returns the arguments required for Mail::send()
677- * used to build the arguments for a mail::send() call
678- *
679- * Usage:
680- * $mailtext = Full email (for example generated by a template)
681- * $decoder = new Mail_mimeDecode($mailtext);
682- * $parts = $decoder->getSendArray();
683- * if (!PEAR::isError($parts) {
684- * list($recipents,$headers,$body) = $parts;
685- * $mail = Mail::factory('smtp');
686- * $mail->send($recipents,$headers,$body);
687- * } else {
688- * echo $parts->message;
689- * }
690- * @return mixed array of recipeint, headers,body or Pear_Error
691- * @access public
692- * @author Alan Knowles <alan@akbkhome.com>
693- */
694- function getSendArray()
695- {
696- // prevent warning if this is not set
697- $this->_decode_headers = FALSE;
698- $headerlist =$this->_parseHeaders($this->_header);
699- $to = "";
700- if (!$headerlist) {
701- return $this->raiseError("Message did not contain headers");
702- }
703- foreach($headerlist as $item) {
704- $header[$item['name']] = $item['value'];
705- switch (strtolower($item['name'])) {
706- case "to":
707- case "cc":
708- case "bcc":
709- $to = ",".$item['value'];
710- default:
711- break;
712- }
713- }
714- if ($to == "") {
715- return $this->raiseError("Message did not contain any recipents");
716- }
717- $to = substr($to,1);
718- return array($to,$header,$this->_body);
719- }
720-
721- /**
722- * Returns a xml copy of the output of
723- * Mail_mimeDecode::decode. Pass the output in as the
724- * argument. This function can be called statically. Eg:
725- *
726- * $output = $obj->decode();
727- * $xml = Mail_mimeDecode::getXML($output);
728- *
729- * The DTD used for this should have been in the package. Or
730- * alternatively you can get it from cvs, or here:
731- * http://www.phpguru.org/xmail/xmail.dtd.
732- *
733- * @param object Input to convert to xml. This should be the
734- * output of the Mail_mimeDecode::decode function
735- * @return string XML version of input
736- * @access public
737- */
738- function getXML($input)
739- {
740- $crlf = "\r\n";
741- $output = '<?xml version=\'1.0\'?>' . $crlf .
742- '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
743- '<email>' . $crlf .
744- Mail_mimeDecode::_getXML($input) .
745- '</email>';
746-
747- return $output;
748- }
749-
750- /**
751- * Function that does the actual conversion to xml. Does a single
752- * mimepart at a time.
753- *
754- * @param object Input to convert to xml. This is a mimepart object.
755- * It may or may not contain subparts.
756- * @param integer Number of tabs to indent
757- * @return string XML version of input
758- * @access private
759- */
760- function _getXML($input, $indent = 1)
761- {
762- $htab = "\t";
763- $crlf = "\r\n";
764- $output = '';
765- $headers = @(array)$input->headers;
766-
767- foreach ($headers as $hdr_name => $hdr_value) {
768-
769- // Multiple headers with this name
770- if (is_array($headers[$hdr_name])) {
771- for ($i = 0; $i < count($hdr_value); $i++) {
772- $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
773- }
774-
775- // Only one header of this sort
776- } else {
777- $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
778- }
779- }
780-
781- if (!empty($input->parts)) {
782- for ($i = 0; $i < count($input->parts); $i++) {
783- $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
784- Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
785- str_repeat($htab, $indent) . '</mimepart>' . $crlf;
786- }
787- } elseif (isset($input->body)) {
788- $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
789- $input->body . ']]></body>' . $crlf;
790- }
791-
792- return $output;
793- }
794-
795- /**
796- * Helper function to _getXML(). Returns xml of a header.
797- *
798- * @param string Name of header
799- * @param string Value of header
800- * @param integer Number of tabs to indent
801- * @return string XML version of input
802- * @access private
803- */
804- function _getXML_helper($hdr_name, $hdr_value, $indent)
805- {
806- $htab = "\t";
807- $crlf = "\r\n";
808- $return = '';
809-
810- $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
811- $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
812-
813- // Sort out any parameters
814- if (!empty($new_hdr_value['other'])) {
815- foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
816- $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
817- str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
818- str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
819- str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
820- }
821-
822- $params = implode('', $params);
823- } else {
824- $params = '';
825- }
826-
827- $return = str_repeat($htab, $indent) . '<header>' . $crlf .
828- str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
829- str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
830- $params .
831- str_repeat($htab, $indent) . '</header>' . $crlf;
832-
833- return $return;
834- }
835-
836-} // End of class
837-?>
1+<?php
2+/**
3+ * The Mail_mimeDecode class is used to decode mail/mime messages
4+ *
5+ * This class will parse a raw mime email and return
6+ * the structure. Returned structure is similar to
7+ * that returned by imap_fetchstructure().
8+ *
9+ * +----------------------------- IMPORTANT ------------------------------+
10+ * | Usage of this class compared to native php extensions such as |
11+ * | mailparse or imap, is slow and may be feature deficient. If available|
12+ * | you are STRONGLY recommended to use the php extensions. |
13+ * +----------------------------------------------------------------------+
14+ *
15+ * Compatible with PHP versions 4 and 5
16+ *
17+ * LICENSE: This LICENSE is in the BSD license style.
18+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
19+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
20+ * All rights reserved.
21+ *
22+ * Redistribution and use in source and binary forms, with or
23+ * without modification, are permitted provided that the following
24+ * conditions are met:
25+ *
26+ * - Redistributions of source code must retain the above copyright
27+ * notice, this list of conditions and the following disclaimer.
28+ * - Redistributions in binary form must reproduce the above copyright
29+ * notice, this list of conditions and the following disclaimer in the
30+ * documentation and/or other materials provided with the distribution.
31+ * - Neither the name of the authors, nor the names of its contributors
32+ * may be used to endorse or promote products derived from this
33+ * software without specific prior written permission.
34+ *
35+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
39+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
45+ * THE POSSIBILITY OF SUCH DAMAGE.
46+ *
47+ * @category Mail
48+ * @package Mail_Mime
49+ * @author Richard Heyes <richard@phpguru.org>
50+ * @author George Schlossnagle <george@omniti.com>
51+ * @author Cipriano Groenendal <cipri@php.net>
52+ * @author Sean Coates <sean@php.net>
53+ * @copyright 2003-2006 PEAR <pear-group@php.net>
54+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
55+ * @version CVS: $Id: mimeDecode.php,v 1.8 2008/07/19 17:46:36 hsur Exp $
56+ * @link http://pear.php.net/package/Mail_mime
57+ */
58+
59+
60+/**
61+ * require PEAR
62+ *
63+ * This package depends on PEAR to raise errors.
64+ */
65+require_once 'PEAR.php';
66+
67+
68+/**
69+ * The Mail_mimeDecode class is used to decode mail/mime messages
70+ *
71+ * This class will parse a raw mime email and return the structure.
72+ * Returned structure is similar to that returned by imap_fetchstructure().
73+ *
74+ * +----------------------------- IMPORTANT ------------------------------+
75+ * | Usage of this class compared to native php extensions such as |
76+ * | mailparse or imap, is slow and may be feature deficient. If available|
77+ * | you are STRONGLY recommended to use the php extensions. |
78+ * +----------------------------------------------------------------------+
79+ *
80+ * @category Mail
81+ * @package Mail_Mime
82+ * @author Richard Heyes <richard@phpguru.org>
83+ * @author George Schlossnagle <george@omniti.com>
84+ * @author Cipriano Groenendal <cipri@php.net>
85+ * @author Sean Coates <sean@php.net>
86+ * @copyright 2003-2006 PEAR <pear-group@php.net>
87+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
88+ * @version Release: @package_version@
89+ * @link http://pear.php.net/package/Mail_mime
90+ */
91+class Mail_mimeDecode extends PEAR
92+{
93+ /**
94+ * The raw email to decode
95+ *
96+ * @var string
97+ * @access private
98+ */
99+ var $_input;
100+
101+ /**
102+ * The header part of the input
103+ *
104+ * @var string
105+ * @access private
106+ */
107+ var $_header;
108+
109+ /**
110+ * The body part of the input
111+ *
112+ * @var string
113+ * @access private
114+ */
115+ var $_body;
116+
117+ /**
118+ * If an error occurs, this is used to store the message
119+ *
120+ * @var string
121+ * @access private
122+ */
123+ var $_error;
124+
125+ /**
126+ * Flag to determine whether to include bodies in the
127+ * returned object.
128+ *
129+ * @var boolean
130+ * @access private
131+ */
132+ var $_include_bodies;
133+
134+ /**
135+ * Flag to determine whether to decode bodies
136+ *
137+ * @var boolean
138+ * @access private
139+ */
140+ var $_decode_bodies;
141+
142+ /**
143+ * Flag to determine whether to decode headers
144+ *
145+ * @var boolean
146+ * @access private
147+ */
148+ var $_decode_headers;
149+
150+ /**
151+ * Constructor.
152+ *
153+ * Sets up the object, initialise the variables, and splits and
154+ * stores the header and body of the input.
155+ *
156+ * @param string The input to decode
157+ * @access public
158+ */
159+ function Mail_mimeDecode($input)
160+ {
161+ list($header, $body) = $this->_splitBodyHeader($input);
162+
163+ $this->_input = $input;
164+ $this->_header = $header;
165+ $this->_body = $body;
166+ $this->_decode_bodies = false;
167+ $this->_include_bodies = true;
168+ }
169+
170+ /**
171+ * Begins the decoding process. If called statically
172+ * it will create an object and call the decode() method
173+ * of it.
174+ *
175+ * @param array An array of various parameters that determine
176+ * various things:
177+ * include_bodies - Whether to include the body in the returned
178+ * object.
179+ * decode_bodies - Whether to decode the bodies
180+ * of the parts. (Transfer encoding)
181+ * decode_headers - Whether to decode headers
182+ * input - If called statically, this will be treated
183+ * as the input
184+ * @return object Decoded results
185+ * @access public
186+ */
187+ function decode($params = null)
188+ {
189+ // determine if this method has been called statically
190+ $isStatic = !(isset($this) && get_class($this) == __CLASS__);
191+
192+ // Have we been called statically?
193+ // If so, create an object and pass details to that.
194+ if ($isStatic AND isset($params['input'])) {
195+
196+ $obj = new Mail_mimeDecode($params['input']);
197+ $structure = $obj->decode($params);
198+
199+ // Called statically but no input
200+ } elseif ($isStatic) {
201+ return PEAR::raiseError('Called statically and no input given');
202+
203+ // Called via an object
204+ } else {
205+ $this->_include_bodies = isset($params['include_bodies']) ?
206+ $params['include_bodies'] : false;
207+ $this->_decode_bodies = isset($params['decode_bodies']) ?
208+ $params['decode_bodies'] : false;
209+ $this->_decode_headers = isset($params['decode_headers']) ?
210+ $params['decode_headers'] : false;
211+
212+ $structure = $this->_decode($this->_header, $this->_body);
213+ if ($structure === false) {
214+ $structure = $this->raiseError($this->_error);
215+ }
216+ }
217+
218+ return $structure;
219+ }
220+
221+ /**
222+ * Performs the decoding. Decodes the body string passed to it
223+ * If it finds certain content-types it will call itself in a
224+ * recursive fashion
225+ *
226+ * @param string Header section
227+ * @param string Body section
228+ * @return object Results of decoding process
229+ * @access private
230+ */
231+ function _decode($headers, $body, $default_ctype = 'text/plain')
232+ {
233+ $return = new stdClass;
234+ $return->headers = array();
235+ $headers = $this->_parseHeaders($headers);
236+
237+ foreach ($headers as $value) {
238+ if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
239+ $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
240+ $return->headers[strtolower($value['name'])][] = $value['value'];
241+
242+ } elseif (isset($return->headers[strtolower($value['name'])])) {
243+ $return->headers[strtolower($value['name'])][] = $value['value'];
244+
245+ } else {
246+ $return->headers[strtolower($value['name'])] = $value['value'];
247+ }
248+ }
249+
250+ reset($headers);
251+ while (list($key, $value) = each($headers)) {
252+ $headers[$key]['name'] = strtolower($headers[$key]['name']);
253+ switch ($headers[$key]['name']) {
254+
255+ case 'content-type':
256+ $content_type = $this->_parseHeaderValue($headers[$key]['value']);
257+
258+ if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
259+ $return->ctype_primary = $regs[1];
260+ $return->ctype_secondary = $regs[2];
261+ }
262+
263+ if (isset($content_type['other'])) {
264+ while (list($p_name, $p_value) = each($content_type['other'])) {
265+ $return->ctype_parameters[$p_name] = $p_value;
266+ }
267+ }
268+ break;
269+
270+ case 'content-disposition':
271+ $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
272+ $return->disposition = $content_disposition['value'];
273+ if (isset($content_disposition['other'])) {
274+ while (list($p_name, $p_value) = each($content_disposition['other'])) {
275+ $return->d_parameters[$p_name] = $p_value;
276+ }
277+ }
278+ break;
279+
280+ case 'content-transfer-encoding':
281+ $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
282+ break;
283+ }
284+ }
285+
286+ if (isset($content_type)) {
287+ switch (strtolower($content_type['value'])) {
288+ case 'text/plain':
289+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
290+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
291+ break;
292+
293+ case 'text/html':
294+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
295+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
296+ break;
297+
298+ case 'multipart/parallel':
299+ case 'multipart/appledouble': // Appledouble mail
300+ case 'multipart/report': // RFC1892
301+ case 'multipart/signed': // PGP
302+ case 'multipart/digest':
303+ case 'multipart/alternative':
304+ case 'multipart/related':
305+ case 'multipart/mixed':
306+ if(!isset($content_type['other']['boundary'])){
307+ $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
308+ return false;
309+ }
310+
311+ $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
312+
313+ $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
314+ for ($i = 0; $i < count($parts); $i++) {
315+ list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
316+ $part = $this->_decode($part_header, $part_body, $default_ctype);
317+ if($part === false)
318+ $part = $this->raiseError($this->_error);
319+ $return->parts[] = $part;
320+ }
321+ break;
322+
323+ case 'message/rfc822':
324+ $obj = &new Mail_mimeDecode($body);
325+ $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
326+ 'decode_bodies' => $this->_decode_bodies,
327+ 'decode_headers' => $this->_decode_headers));
328+ unset($obj);
329+ break;
330+
331+ default:
332+ if(!isset($content_transfer_encoding['value']))
333+ $content_transfer_encoding['value'] = '7bit';
334+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
335+ break;
336+ }
337+
338+ } else {
339+ $ctype = explode('/', $default_ctype);
340+ $return->ctype_primary = $ctype[0];
341+ $return->ctype_secondary = $ctype[1];
342+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
343+ }
344+
345+ return $return;
346+ }
347+
348+ /**
349+ * Given the output of the above function, this will return an
350+ * array of references to the parts, indexed by mime number.
351+ *
352+ * @param object $structure The structure to go through
353+ * @param string $mime_number Internal use only.
354+ * @return array Mime numbers
355+ */
356+ function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
357+ {
358+ $return = array();
359+ if (!empty($structure->parts)) {
360+ if ($mime_number != '') {
361+ $structure->mime_id = $prepend . $mime_number;
362+ $return[$prepend . $mime_number] = &$structure;
363+ }
364+ for ($i = 0; $i < count($structure->parts); $i++) {
365+
366+
367+ if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
368+ $prepend = $prepend . $mime_number . '.';
369+ $_mime_number = '';
370+ } else {
371+ $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
372+ }
373+
374+ $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
375+ foreach ($arr as $key => $val) {
376+ $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
377+ }
378+ }
379+ } else {
380+ if ($mime_number == '') {
381+ $mime_number = '1';
382+ }
383+ $structure->mime_id = $prepend . $mime_number;
384+ $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
385+ }
386+
387+ return $return;
388+ }
389+
390+ /**
391+ * Given a string containing a header and body
392+ * section, this function will split them (at the first
393+ * blank line) and return them.
394+ *
395+ * @param string Input to split apart
396+ * @return array Contains header and body section
397+ * @access private
398+ */
399+ function _splitBodyHeader($input)
400+ {
401+ if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
402+ return array($match[1], $match[2]);
403+ }
404+ $this->_error = 'Could not split header and body';
405+ return false;
406+ }
407+
408+ /**
409+ * Parse headers given in $input and return
410+ * as assoc array.
411+ *
412+ * @param string Headers to parse
413+ * @return array Contains parsed headers
414+ * @access private
415+ */
416+ function _parseHeaders($input)
417+ {
418+
419+ if ($input !== '') {
420+ // Unfold the input
421+ $input = preg_replace("/\r?\n/", "\r\n", $input);
422+ $input = preg_replace("/\r\n(\t| )+/", ' ', $input);
423+ $headers = explode("\r\n", trim($input));
424+
425+ foreach ($headers as $value) {
426+ $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
427+ $hdr_value = substr($value, $pos+1);
428+ if($hdr_value[0] == ' ')
429+ $hdr_value = substr($hdr_value, 1);
430+
431+ $return[] = array(
432+ 'name' => $hdr_name,
433+ 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
434+ );
435+ }
436+ } else {
437+ $return = array();
438+ }
439+
440+ return $return;
441+ }
442+
443+ /**
444+ * Function to parse a header value,
445+ * extract first part, and any secondary
446+ * parts (after ;) This function is not as
447+ * robust as it could be. Eg. header comments
448+ * in the wrong place will probably break it.
449+ *
450+ * @param string Header value to parse
451+ * @return array Contains parsed result
452+ * @access private
453+ */
454+ function _parseHeaderValue($input)
455+ {
456+
457+ if (($pos = strpos($input, ';')) !== false) {
458+
459+ $return['value'] = trim(substr($input, 0, $pos));
460+ $input = trim(substr($input, $pos+1));
461+
462+ if (strlen($input) > 0) {
463+
464+ // This splits on a semi-colon, if there's no preceeding backslash
465+ // Now works with quoted values; had to glue the \; breaks in PHP
466+ // the regex is already bordering on incomprehensible
467+ $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
468+ preg_match_all($splitRegex, $input, $matches);
469+ $parameters = array();
470+ for ($i=0; $i<count($matches[0]); $i++) {
471+ $param = $matches[0][$i];
472+ while (substr($param, -2) == '\;') {
473+ $param .= $matches[0][++$i];
474+ }
475+ $parameters[] = $param;
476+ }
477+
478+ for ($i = 0; $i < count($parameters); $i++) {
479+ $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
480+ $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
481+ if ($param_value[0] == '"') {
482+ $param_value = substr($param_value, 1, -1);
483+ }
484+ $return['other'][$param_name] = $param_value;
485+ $return['other'][strtolower($param_name)] = $param_value;
486+ }
487+ }
488+ } else {
489+ $return['value'] = trim($input);
490+ }
491+
492+ return $return;
493+ }
494+
495+ /**
496+ * This function splits the input based
497+ * on the given boundary
498+ *
499+ * @param string Input to parse
500+ * @return array Contains array of resulting mime parts
501+ * @access private
502+ */
503+ function _boundarySplit($input, $boundary)
504+ {
505+ $parts = array();
506+
507+ $bs_possible = substr($boundary, 2, -2);
508+ $bs_check = '\"' . $bs_possible . '\"';
509+
510+ if ($boundary == $bs_check) {
511+ $boundary = $bs_possible;
512+ }
513+
514+ $tmp = explode('--' . $boundary, $input);
515+
516+ for ($i = 1; $i < count($tmp) - 1; $i++) {
517+ $parts[] = $tmp[$i];
518+ }
519+
520+ return $parts;
521+ }
522+
523+ /**
524+ * Given a header, this function will decode it
525+ * according to RFC2047. Probably not *exactly*
526+ * conformant, but it does pass all the given
527+ * examples (in RFC2047).
528+ *
529+ * @param string Input header value to decode
530+ * @return string Decoded header value
531+ * @access private
532+ */
533+ function _decodeHeader($input)
534+ {
535+ // Remove white space between encoded-words
536+ $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
537+
538+ // For each encoded-word...
539+ while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
540+
541+ $encoded = $matches[1];
542+ $charset = $matches[2];
543+ $encoding = $matches[3];
544+ $text = $matches[4];
545+
546+ switch (strtolower($encoding)) {
547+ case 'b':
548+ $text = base64_decode($text);
549+ break;
550+
551+ case 'q':
552+ $text = str_replace('_', ' ', $text);
553+ preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
554+ foreach($matches[1] as $value)
555+ $text = str_replace('='.$value, chr(hexdec($value)), $text);
556+ break;
557+ }
558+
559+ $input = str_replace($encoded, $text, $input);
560+ }
561+
562+ return $input;
563+ }
564+
565+ /**
566+ * Given a body string and an encoding type,
567+ * this function will decode and return it.
568+ *
569+ * @param string Input body to decode
570+ * @param string Encoding type to use.
571+ * @return string Decoded body
572+ * @access private
573+ */
574+ function _decodeBody($input, $encoding = '7bit')
575+ {
576+ switch (strtolower($encoding)) {
577+ case '7bit':
578+ return $input;
579+ break;
580+
581+ case 'quoted-printable':
582+ return $this->_quotedPrintableDecode($input);
583+ break;
584+
585+ case 'base64':
586+ return base64_decode($input);
587+ break;
588+
589+ default:
590+ return $input;
591+ }
592+ }
593+
594+ /**
595+ * Given a quoted-printable string, this
596+ * function will decode and return it.
597+ *
598+ * @param string Input body to decode
599+ * @return string Decoded body
600+ * @access private
601+ */
602+ function _quotedPrintableDecode($input)
603+ {
604+ // Remove soft line breaks
605+ $input = preg_replace("/=\r?\n/", '', $input);
606+
607+ // Replace encoded characters
608+ $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
609+
610+ return $input;
611+ }
612+
613+ /**
614+ * Checks the input for uuencoded files and returns
615+ * an array of them. Can be called statically, eg:
616+ *
617+ * $files =& Mail_mimeDecode::uudecode($some_text);
618+ *
619+ * It will check for the begin 666 ... end syntax
620+ * however and won't just blindly decode whatever you
621+ * pass it.
622+ *
623+ * @param string Input body to look for attahcments in
624+ * @return array Decoded bodies, filenames and permissions
625+ * @access public
626+ * @author Unknown
627+ */
628+ function &uudecode($input)
629+ {
630+ // Find all uuencoded sections
631+ preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
632+
633+ for ($j = 0; $j < count($matches[3]); $j++) {
634+
635+ $str = $matches[3][$j];
636+ $filename = $matches[2][$j];
637+ $fileperm = $matches[1][$j];
638+
639+ $file = '';
640+ $str = preg_split("/\r?\n/", trim($str));
641+ $strlen = count($str);
642+
643+ for ($i = 0; $i < $strlen; $i++) {
644+ $pos = 1;
645+ $d = 0;
646+ $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
647+
648+ while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
649+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
650+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
651+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
652+ $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
653+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
654+
655+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
656+
657+ $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077));
658+
659+ $pos += 4;
660+ $d += 3;
661+ }
662+
663+ if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
664+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
665+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
666+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
667+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
668+
669+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
670+
671+ $pos += 3;
672+ $d += 2;
673+ }
674+
675+ if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
676+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
677+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
678+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
679+
680+ }
681+ }
682+ $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
683+ }
684+
685+ return $files;
686+ }
687+
688+ /**
689+ * getSendArray() returns the arguments required for Mail::send()
690+ * used to build the arguments for a mail::send() call
691+ *
692+ * Usage:
693+ * $mailtext = Full email (for example generated by a template)
694+ * $decoder = new Mail_mimeDecode($mailtext);
695+ * $parts = $decoder->getSendArray();
696+ * if (!PEAR::isError($parts) {
697+ * list($recipents,$headers,$body) = $parts;
698+ * $mail = Mail::factory('smtp');
699+ * $mail->send($recipents,$headers,$body);
700+ * } else {
701+ * echo $parts->message;
702+ * }
703+ * @return mixed array of recipeint, headers,body or Pear_Error
704+ * @access public
705+ * @author Alan Knowles <alan@akbkhome.com>
706+ */
707+ function getSendArray()
708+ {
709+ // prevent warning if this is not set
710+ $this->_decode_headers = FALSE;
711+ $headerlist =$this->_parseHeaders($this->_header);
712+ $to = "";
713+ if (!$headerlist) {
714+ return $this->raiseError("Message did not contain headers");
715+ }
716+ foreach($headerlist as $item) {
717+ $header[$item['name']] = $item['value'];
718+ switch (strtolower($item['name'])) {
719+ case "to":
720+ case "cc":
721+ case "bcc":
722+ $to = ",".$item['value'];
723+ default:
724+ break;
725+ }
726+ }
727+ if ($to == "") {
728+ return $this->raiseError("Message did not contain any recipents");
729+ }
730+ $to = substr($to,1);
731+ return array($to,$header,$this->_body);
732+ }
733+
734+ /**
735+ * Returns a xml copy of the output of
736+ * Mail_mimeDecode::decode. Pass the output in as the
737+ * argument. This function can be called statically. Eg:
738+ *
739+ * $output = $obj->decode();
740+ * $xml = Mail_mimeDecode::getXML($output);
741+ *
742+ * The DTD used for this should have been in the package. Or
743+ * alternatively you can get it from cvs, or here:
744+ * http://www.phpguru.org/xmail/xmail.dtd.
745+ *
746+ * @param object Input to convert to xml. This should be the
747+ * output of the Mail_mimeDecode::decode function
748+ * @return string XML version of input
749+ * @access public
750+ */
751+ function getXML($input)
752+ {
753+ $crlf = "\r\n";
754+ $output = '<?xml version=\'1.0\'?>' . $crlf .
755+ '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
756+ '<email>' . $crlf .
757+ Mail_mimeDecode::_getXML($input) .
758+ '</email>';
759+
760+ return $output;
761+ }
762+
763+ /**
764+ * Function that does the actual conversion to xml. Does a single
765+ * mimepart at a time.
766+ *
767+ * @param object Input to convert to xml. This is a mimepart object.
768+ * It may or may not contain subparts.
769+ * @param integer Number of tabs to indent
770+ * @return string XML version of input
771+ * @access private
772+ */
773+ function _getXML($input, $indent = 1)
774+ {
775+ $htab = "\t";
776+ $crlf = "\r\n";
777+ $output = '';
778+ $headers = @(array)$input->headers;
779+
780+ foreach ($headers as $hdr_name => $hdr_value) {
781+
782+ // Multiple headers with this name
783+ if (is_array($headers[$hdr_name])) {
784+ for ($i = 0; $i < count($hdr_value); $i++) {
785+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
786+ }
787+
788+ // Only one header of this sort
789+ } else {
790+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
791+ }
792+ }
793+
794+ if (!empty($input->parts)) {
795+ for ($i = 0; $i < count($input->parts); $i++) {
796+ $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
797+ Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
798+ str_repeat($htab, $indent) . '</mimepart>' . $crlf;
799+ }
800+ } elseif (isset($input->body)) {
801+ $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
802+ $input->body . ']]></body>' . $crlf;
803+ }
804+
805+ return $output;
806+ }
807+
808+ /**
809+ * Helper function to _getXML(). Returns xml of a header.
810+ *
811+ * @param string Name of header
812+ * @param string Value of header
813+ * @param integer Number of tabs to indent
814+ * @return string XML version of input
815+ * @access private
816+ */
817+ function _getXML_helper($hdr_name, $hdr_value, $indent)
818+ {
819+ $htab = "\t";
820+ $crlf = "\r\n";
821+ $return = '';
822+
823+ $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
824+ $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
825+
826+ // Sort out any parameters
827+ if (!empty($new_hdr_value['other'])) {
828+ foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
829+ $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
830+ str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
831+ str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
832+ str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
833+ }
834+
835+ $params = implode('', $params);
836+ } else {
837+ $params = '';
838+ }
839+
840+ $return = str_repeat($htab, $indent) . '<header>' . $crlf .
841+ str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
842+ str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
843+ $params .
844+ str_repeat($htab, $indent) . '</header>' . $crlf;
845+
846+ return $return;
847+ }
848+
849+} // End of class
--- a/trunk/NP_Moblog/sharedlibs/Net/POP3.php
+++ b/trunk/NP_Moblog/sharedlibs/Net/POP3.php
@@ -1,1226 +1,1226 @@
1-<?php
2-// +-----------------------------------------------------------------------+
3-// | Copyright (c) 2002, Richard Heyes |
4-// | All rights reserved. |
5-// | |
6-// | Redistribution and use in source and binary forms, with or without |
7-// | modification, are permitted provided that the following conditions |
8-// | are met: |
9-// | |
10-// | o Redistributions of source code must retain the above copyright |
11-// | notice, this list of conditions and the following disclaimer. |
12-// | o Redistributions in binary form must reproduce the above copyright |
13-// | notice, this list of conditions and the following disclaimer in the |
14-// | documentation and/or other materials provided with the distribution.|
15-// | o The names of the authors may not be used to endorse or promote |
16-// | products derived from this software without specific prior written |
17-// | permission. |
18-// | |
19-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30-// | |
31-// +-----------------------------------------------------------------------+
32-// | Author: Richard Heyes <richard@phpguru.org> |
33-// | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar> |
34-// +-----------------------------------------------------------------------+
35-//
36-// $Id: POP3.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
37-
38-require_once('Net/Socket.php');
39-
40-
41-
42-/**
43-* +----------------------------- IMPORTANT ------------------------------+
44-* | Usage of this class compared to native php extensions such as IMAP |
45-* | is slow and may be feature deficient. If available you are STRONGLY |
46-* | recommended to use the php extensions. |
47-* +----------------------------------------------------------------------+
48-*
49-* POP3 Access Class
50-*
51-* For usage see the example script
52-*/
53-
54-define('NET_POP3_STATE_DISCONNECTED', 1, true);
55-define('NET_POP3_STATE_AUTHORISATION', 2, true);
56-define('NET_POP3_STATE_TRANSACTION', 4, true);
57-
58-class Net_POP3 {
59-
60- /*
61- * Some basic information about the mail drop
62- * garnered from the STAT command
63- *
64- * @var array
65- */
66- var $_maildrop;
67-
68- /*
69- * Used for APOP to store the timestamp
70- *
71- * @var string
72- */
73- var $_timestamp;
74-
75- /*
76- * Timeout that is passed to the socket object
77- *
78- * @var integer
79- */
80- var $_timeout;
81-
82- /*
83- * Socket object
84- *
85- * @var object
86- */
87- var $_socket;
88-
89- /*
90- * Current state of the connection. Used with the
91- * constants defined above.
92- *
93- * @var integer
94- */
95- var $_state;
96-
97- /*
98- * Hostname to connect to
99- *
100- * @var string
101- */
102- var $_host;
103-
104- /*
105- * Port to connect to
106- *
107- * @var integer
108- */
109- var $_port;
110-
111- /**
112- * To allow class debuging
113- * @var boolean
114- */
115- var $_debug = false;
116-
117-
118- /**
119- * The auth methods this class support
120- * @var array
121- */
122- //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
123- //Disabling DIGEST-MD5 for now
124- var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
125- //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
126- //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
127-
128-
129- /**
130- * The auth methods this class support
131- * @var array
132- */
133- var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
134-
135-
136- /**
137- * The capability response
138- * @var array
139- */
140- var $_capability;
141-
142- /*
143- * Constructor. Sets up the object variables, and instantiates
144- * the socket object.
145- *
146- */
147-
148-
149- function Net_POP3()
150- {
151- $this->_timestamp = ''; // Used for APOP
152- $this->_maildrop = array();
153- $this->_timeout = 3;
154- $this->_state = NET_POP3_STATE_DISCONNECTED;
155- $this->_socket =& new Net_Socket();
156- /*
157- * Include the Auth_SASL package. If the package is not available,
158- * we disable the authentication methods that depend upon it.
159- */
160- if ((@include_once 'Auth/SASL.php') == false) {
161- if($this->_debug){
162- echo "AUTH_SASL NOT PRESENT!\n";
163- }
164- foreach($this->supportedSASLAuthMethods as $SASLMethod){
165- $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
166- if($this->_debug){
167- echo "DISABLING METHOD $SASLMethod\n";
168- }
169- unset($this->supportedAuthMethods[$pos]);
170- }
171- }
172-
173-
174-
175- }
176-
177-
178- /**
179- * Handles the errors the class can find
180- * on the server
181- *
182- * @access private
183- * @return PEAR_Error
184- */
185-
186- function _raiseError($msg, $code =-1)
187- {
188- include_once 'PEAR.php';
189- return PEAR::raiseError($msg, $code);
190- }
191-
192-
193-
194- /*
195- * Connects to the given host on the given port.
196- * Also looks for the timestamp in the greeting
197- * needed for APOP authentication
198- *
199- * @param string $host Hostname/IP address to connect to
200- * @param string $port Port to use to connect to on host
201- * @return bool Success/Failure
202- */
203- function connect($host = 'localhost', $port = 110)
204- {
205- $this->_host = $host;
206- $this->_port = $port;
207-
208- $result = $this->_socket->connect($host, $port, false, $this->_timeout);
209- if ($result === true) {
210- $data = $this->_recvLn();
211-
212- if( $this->_checkResponse($data) ){
213- // if the response begins with '+OK' ...
214-// if (@substr(strtoupper($data), 0, 3) == '+OK') {
215- // Check for string matching apop timestamp
216- if (preg_match('/<.+@.+>/U', $data, $matches)) {
217- $this->_timestamp = $matches[0];
218- }
219- $this->_maildrop = array();
220- $this->_state = NET_POP3_STATE_AUTHORISATION;
221-
222- return true;
223- }
224- }
225-
226- $this->_socket->disconnect();
227- return false;
228- }
229-
230- /*
231- * Disconnect function. Sends the QUIT command
232- * and closes the socket.
233- *
234- * @return bool Success/Failure
235- */
236- function disconnect()
237- {
238- return $this->_cmdQuit();
239- }
240-
241- /*
242- * Performs the login procedure. If there is a timestamp
243- * stored, APOP will be tried first, then basic USER/PASS.
244- *
245- * @param string $user Username to use
246- * @param string $pass Password to use
247- * @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");
248- * Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
249- * @return mixed true on Success/ PEAR_ERROR on error
250- */
251- function login($user, $pass, $apop = true)
252- {
253- if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
254-
255- if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){
256- return $ret;
257- }
258- if( ! PEAR::isError($ret)){
259- $this->_state = NET_POP3_STATE_TRANSACTION;
260- return true;
261- }
262-
263- }
264- return $this->_raiseError('Generic login error' , 1);
265- }
266-
267-
268-
269- /**
270- * Parses the response from the capability command. Stores
271- * the result in $this->_capability
272- *
273- * @access private
274- */
275- function _parseCapability()
276- {
277-
278- if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){
279- $data = $this->_getMultiline();
280- }else {
281- // CAPA command not supported, reset data var
282- // to avoid Notice errors of preg_split on an object
283- $data = '';
284- }
285- $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
286-
287- for ($i = 0; $i < count($data); $i++) {
288-
289- $capa='';
290- if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
291-
292- $capa=strtolower($matches[1]);
293- switch ($capa) {
294- case 'implementation':
295- $this->_capability['implementation'] = $matches[3];
296- break;
297- case 'sasl':
298- $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
299- break;
300- default :
301- $this->_capability[$capa] = $matches[2];
302- break;
303- }
304- }
305- }
306- }
307-
308-
309-
310-
311- /**
312- * Returns the name of the best authentication method that the server
313- * has advertised.
314- *
315- * @param string if !=null,authenticate with this method ($userMethod).
316- *
317- * @return mixed Returns a string containing the name of the best
318- * supported authentication method or a PEAR_Error object
319- * if a failure condition is encountered.
320- * @access private
321- * @since 1.0
322- */
323- function _getBestAuthMethod($userMethod = null)
324- {
325-
326-/*
327- return 'USER';
328- return 'APOP';
329- return 'DIGEST-MD5';
330- return 'CRAM-MD5';
331-*/
332-
333-
334- $this->_parseCapability();
335-
336- //unset($this->_capability['sasl']);
337-
338- if( isset($this->_capability['sasl']) ){
339- $serverMethods=$this->_capability['sasl'];
340- }else{
341- $serverMethods=array('USER');
342- // Check for timestamp before attempting APOP
343- if ($this->_timestamp != null)
344- {
345- $serverMethods[] = 'APOP';
346- }
347- }
348-
349- if($userMethod !== null && $userMethod !== true ){
350- $methods = array();
351- $methods[] = $userMethod;
352- return $userMethod;
353- }else{
354- $methods = $this->supportedAuthMethods;
355- }
356-
357- if( ($methods != null) && ($serverMethods != null)){
358-
359- foreach ( $methods as $method ) {
360-
361- if ( in_array( $method , $serverMethods ) ) {
362- return $method;
363- }
364- }
365- $serverMethods=implode(',' , $serverMethods );
366- $myMethods=implode(',' ,$this->supportedAuthMethods);
367- return $this->_raiseError("$method NOT supported authentication method!. This server " .
368- "supports these methods: $serverMethods, but I support $myMethods");
369- }else{
370- return $this->_raiseError("This server don't support any Auth methods");
371- }
372- }
373-
374-
375-
376-
377-
378-
379- /* Handles the authentication using any known method
380- *
381- * @param string The userid to authenticate as.
382- * @param string The password to authenticate with.
383- * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
384- *
385- * @return mixed string or PEAR_Error
386- *
387- * @access private
388- * @since 1.0
389- */
390- function _cmdAuthenticate($uid , $pwd , $userMethod = null )
391- {
392-
393-
394- if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
395- return $method;
396- }
397-
398- switch ($method) {
399- case 'DIGEST-MD5':
400- $result = $this->_authDigest_MD5( $uid , $pwd );
401- break;
402- case 'CRAM-MD5':
403- $result = $this->_authCRAM_MD5( $uid , $pwd );
404- break;
405- case 'LOGIN':
406- $result = $this->_authLOGIN( $uid , $pwd );
407- break;
408- case 'PLAIN':
409- $result = $this->_authPLAIN( $uid , $pwd );
410- break;
411- case 'APOP':
412- $result = $this->_cmdApop( $uid , $pwd );
413- // if APOP fails fallback to USER auth
414- if( PEAR::isError( $result ) ){
415- //echo "APOP FAILED!!!\n";
416- $result=$this->_authUSER( $uid , $pwd );
417- }
418- break;
419- case 'USER':
420- $result = $this->_authUSER( $uid , $pwd );
421- break;
422-
423-
424- default :
425- $result = $this->_raiseError( "$method is not a supported authentication method" );
426- break;
427- }
428- return $result;
429- }
430-
431-
432-
433-
434- /* Authenticates the user using the USER-PASS method.
435- *
436- * @param string The userid to authenticate as.
437- * @param string The password to authenticate with.
438- *
439- * @return mixed true on success or PEAR_Error on failure
440- *
441- * @access private
442- * @since 1.0
443- */
444- function _authUSER($user, $pass )
445- {
446- if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){
447- return $ret;
448- }
449- if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){
450- return $ret;
451- }
452- return true;
453- }
454-
455-
456-
457-
458-
459-
460-
461-
462- /* Authenticates the user using the PLAIN method.
463- *
464- * @param string The userid to authenticate as.
465- * @param string The password to authenticate with.
466- *
467- * @return array Returns an array containing the response
468- *
469- * @access private
470- * @since 1.0
471- */
472- function _authPLAIN($user, $pass )
473- {
474- $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );
475-
476- if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {
477- return $ret;
478- }
479- if ( PEAR::isError( $challenge = $this->_recvLn() ) ){
480- return $challenge;
481- }
482- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
483- return $ret;
484- }
485-
486- return true;
487- }
488-
489-
490-
491- /* Authenticates the user using the PLAIN method.
492- *
493- * @param string The userid to authenticate as.
494- * @param string The password to authenticate with.
495- *
496- * @return array Returns an array containing the response
497- *
498- * @access private
499- * @since 1.0
500- */
501- function _authLOGIN($user, $pass )
502- {
503- $this->_send('AUTH LOGIN');
504-
505- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
506- return $challenge;
507- }
508- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
509- return $ret;
510- }
511-
512-
513- if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {
514- return $ret;
515- }
516-
517- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
518- return $challenge;
519- }
520- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
521- return $ret;
522- }
523-
524- if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {
525- return $ret;
526- }
527-
528- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
529- return $challenge;
530- }
531- return $this->_checkResponse($challenge);
532- }
533-
534-
535-
536-
537-
538- /* Authenticates the user using the CRAM-MD5 method.
539- *
540- * @param string The userid to authenticate as.
541- * @param string The password to authenticate with.
542- *
543- * @return array Returns an array containing the response
544- *
545- * @access private
546- * @since 1.0
547- */
548- function _authCRAM_MD5($uid, $pwd )
549- {
550- if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {
551- return $ret;
552- }
553-
554- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
555- return $challenge;
556- }
557- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
558- return $ret;
559- }
560-
561- // remove '+ '
562-
563- $challenge=substr($challenge,2);
564-
565- $challenge = base64_decode( $challenge );
566-
567- $cram = &Auth_SASL::factory('crammd5');
568- $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
569-
570-
571- if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
572- return $error;
573- }
574- if ( PEAR::isError( $ret = $this->_recvLn() ) ) {
575- return $ret;
576- }
577- //echo "RET:$ret\n";
578- return $this->_checkResponse($ret);
579- }
580-
581-
582-
583- /* Authenticates the user using the DIGEST-MD5 method.
584- *
585- * @param string The userid to authenticate as.
586- * @param string The password to authenticate with.
587- * @param string The efective user
588- *
589- * @return array Returns an array containing the response
590- *
591- * @access private
592- * @since 1.0
593- */
594- function _authDigest_MD5($uid, $pwd)
595- {
596- if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {
597- return $ret;
598- }
599-
600- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
601- return $challenge;
602- }
603- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
604- return $ret;
605- }
606-
607- // remove '+ '
608- $challenge=substr($challenge,2);
609-
610- $challenge = base64_decode( $challenge );
611- $digest = &Auth_SASL::factory('digestmd5');
612- $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));
613-
614- if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
615- return $error;
616- }
617-
618- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
619- return $challenge;
620- }
621- if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
622- return $ret;
623- }
624- /*
625- * We don't use the protocol's third step because POP3 doesn't allow
626- * subsequent authentication, so we just silently ignore it.
627- */
628-
629- if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {
630- return $challenge ;
631- }
632-
633- if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
634- return $challenge;
635- }
636-
637- return $this->_checkResponse($challenge);
638-
639-
640- }
641-
642-
643-
644-
645-
646-
647-
648-
649-
650-
651- /*
652- * Sends the APOP command
653- *
654- * @param $user Username to send
655- * @param $pass Password to send
656- * @return bool Success/Failure
657- */
658- function _cmdApop($user, $pass)
659- {
660- if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
661-
662- if (!empty($this->_timestamp)) {
663- if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){
664- return $data;
665- }
666- $this->_state = NET_POP3_STATE_TRANSACTION;
667- return true;
668- }
669- }
670- return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
671- }
672-
673-
674-
675-
676-
677-
678-
679-
680-
681-
682-
683-
684-
685-
686-
687- /*
688- * Returns the raw headers of the specified message.
689- *
690- * @param integer $msg_id Message number
691- * @return mixed Either raw headers or false on error
692- */
693- function getRawHeaders($msg_id)
694- {
695- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
696- return $this->_cmdTop($msg_id, 0);
697- }
698-
699- return false;
700- }
701-
702- /*
703- * Returns the headers of the specified message in an
704- * associative array. Array keys are the header names, array
705- * values are the header values. In the case of multiple headers
706- * having the same names, eg Received:, the array value will be
707- * an indexed array of all the header values.
708- *
709- * @param integer $msg_id Message number
710- * @return mixed Either array of headers or false on error
711- */
712- function getParsedHeaders($msg_id)
713- {
714- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
715-
716- $raw_headers = rtrim($this->getRawHeaders($msg_id));
717-
718- $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
719- $raw_headers = explode("\r\n", $raw_headers);
720- foreach ($raw_headers as $value) {
721- $name = substr($value, 0, $pos = strpos($value, ':'));
722- $value = ltrim(substr($value, $pos + 1));
723- if (isset($headers[$name]) AND is_array($headers[$name])) {
724- $headers[$name][] = $value;
725- } elseif (isset($headers[$name])) {
726- $headers[$name] = array($headers[$name], $value);
727- } else {
728- $headers[$name] = $value;
729- }
730- }
731-
732- return $headers;
733- }
734-
735- return false;
736- }
737-
738- /*
739- * Returns the body of the message with given message number.
740- *
741- * @param integer $msg_id Message number
742- * @return mixed Either message body or false on error
743- */
744- function getBody($msg_id)
745- {
746- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
747- $msg = $this->_cmdRetr($msg_id);
748- return substr($msg, strpos($msg, "\r\n\r\n")+4);
749- }
750-
751- return false;
752- }
753-
754- /*
755- * Returns the entire message with given message number.
756- *
757- * @param integer $msg_id Message number
758- * @return mixed Either entire message or false on error
759- */
760- function getMsg($msg_id)
761- {
762- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
763- return $this->_cmdRetr($msg_id);
764- }
765-
766- return false;
767- }
768-
769- /*
770- * Returns the size of the maildrop
771- *
772- * @return mixed Either size of maildrop or false on error
773- */
774- function getSize()
775- {
776- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
777- if (isset($this->_maildrop['size'])) {
778- return $this->_maildrop['size'];
779- } else {
780- list(, $size) = $this->_cmdStat();
781- return $size;
782- }
783- }
784-
785- return false;
786- }
787-
788- /*
789- * Returns number of messages in this maildrop
790- *
791- * @return mixed Either number of messages or false on error
792- */
793- function numMsg()
794- {
795- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
796- if (isset($this->_maildrop['num_msg'])) {
797- return $this->_maildrop['num_msg'];
798- } else {
799- list($num_msg, ) = $this->_cmdStat();
800- return $num_msg;
801- }
802- }
803-
804- return false;
805- }
806-
807- /*
808- * Marks a message for deletion. Only will be deleted if the
809- * disconnect() method is called.
810- *
811- * @param integer $msg_id Message to delete
812- * @return bool Success/Failure
813- */
814- function deleteMsg($msg_id)
815- {
816- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
817- return $this->_cmdDele($msg_id);
818- }
819-
820- return false;
821- }
822-
823- /*
824- * Combination of LIST/UIDL commands, returns an array
825- * of data
826- *
827- * @param integer $msg_id Optional message number
828- * @return mixed Array of data or false on error
829- */
830- function getListing($msg_id = null)
831- {
832-
833- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
834- if (!isset($msg_id)){
835-
836- $list=array();
837- if ($list = $this->_cmdList()) {
838- if ($uidl = $this->_cmdUidl()) {
839- foreach ($uidl as $i => $value) {
840- $list[$i]['uidl'] = $value['uidl'];
841- }
842- }
843- return $list;
844- }else{
845- return array();
846- }
847- } else {
848- if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {
849- return array_merge($list, $uidl);
850- }
851- }
852- }
853-
854- return false;
855- }
856-
857- /*
858- * Sends the USER command
859- *
860- * @param string $user Username to send
861- * @return bool Success/Failure
862- */
863- function _cmdUser($user)
864- {
865- if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
866- return $this->_sendCmd('USER ' . $user);
867- }
868- return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
869- }
870-
871-
872- /*
873- * Sends the PASS command
874- *
875- * @param string $pass Password to send
876- * @return bool Success/Failure
877- */
878- function _cmdPass($pass)
879- {
880- if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
881- return $this->_sendCmd('PASS ' . $pass);
882- }
883- return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
884- }
885-
886-
887- /*
888- * Sends the STAT command
889- *
890- * @return mixed Indexed array of number of messages and
891- * maildrop size, or false on error.
892- */
893- function _cmdStat()
894- {
895- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
896- if(!PEAR::isError($data = $this->_sendCmd('STAT'))){
897- sscanf($data, '+OK %d %d', $msg_num, $size);
898- $this->_maildrop['num_msg'] = $msg_num;
899- $this->_maildrop['size'] = $size;
900-
901- return array($msg_num, $size);
902- }
903- }
904- return false;
905- }
906-
907-
908- /*
909- * Sends the LIST command
910- *
911- * @param integer $msg_id Optional message number
912- * @return mixed Indexed array of msg_id/msg size or
913- * false on error
914- */
915- function _cmdList($msg_id = null)
916- {
917- $return=array();
918- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
919- if (!isset($msg_id)) {
920- if(!PEAR::isError($data = $this->_sendCmd('LIST') )){
921- $data = $this->_getMultiline();
922- $data = explode("\r\n", $data);
923- foreach ($data as $line) {
924- if($line !=''){
925- sscanf($line, '%s %s', $msg_id, $size);
926- $return[] = array('msg_id' => $msg_id, 'size' => $size);
927- }
928- }
929- return $return;
930- }
931- } else {
932- if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){
933- if($data!=''){
934- sscanf($data, '+OK %d %d', $msg_id, $size);
935- return array('msg_id' => $msg_id, 'size' => $size);
936- }
937- return array();
938- }
939- }
940- }
941-
942-
943- return false;
944- }
945-
946-
947- /*
948- * Sends the RETR command
949- *
950- * @param integer $msg_id The message number to retrieve
951- * @return mixed The message or false on error
952- */
953- function _cmdRetr($msg_id)
954- {
955- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
956- if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){
957- $data = $this->_getMultiline();
958- return $data;
959- }
960- }
961-
962- return false;
963- }
964-
965-
966- /*
967- * Sends the DELE command
968- *
969- * @param integer $msg_id Message number to mark as deleted
970- * @return bool Success/Failure
971- */
972- function _cmdDele($msg_id)
973- {
974- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
975- return $this->_sendCmd('DELE ' . $msg_id);
976- }
977-
978- return false;
979- }
980-
981-
982- /*
983- * Sends the NOOP command
984- *
985- * @return bool Success/Failure
986- */
987- function _cmdNoop()
988- {
989- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
990- if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){
991- return true;
992- }
993- }
994-
995- return false;
996- }
997-
998- /*
999- * Sends the RSET command
1000- *
1001- * @return bool Success/Failure
1002- */
1003- function _cmdRset()
1004- {
1005- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1006- if(!PEAR::isError($data = $this->_sendCmd('RSET'))){
1007- return true;
1008- }
1009- }
1010-
1011- return false;
1012- }
1013-
1014- /*
1015- * Sends the QUIT command
1016- *
1017- * @return bool Success/Failure
1018- */
1019- function _cmdQuit()
1020- {
1021- $data = $this->_sendCmd('QUIT');
1022- $this->_state = NET_POP3_STATE_DISCONNECTED;
1023- $this->_socket->disconnect();
1024-
1025- return (bool)$data;
1026- }
1027-
1028-
1029- /*
1030- * Sends the TOP command
1031- *
1032- * @param integer $msg_id Message number
1033- * @param integer $num_lines Number of lines to retrieve
1034- * @return mixed Message data or false on error
1035- */
1036- function _cmdTop($msg_id, $num_lines)
1037- {
1038- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1039-
1040- if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){
1041- return $this->_getMultiline();
1042- }
1043- }
1044-
1045- return false;
1046- }
1047-
1048- /*
1049- * Sends the UIDL command
1050- *
1051- * @param integer $msg_id Message number
1052- * @return mixed indexed array of msg_id/uidl or false on error
1053- */
1054- function _cmdUidl($msg_id = null)
1055- {
1056- if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1057-
1058- if (!isset($msg_id)) {
1059- if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){
1060- $data = $this->_getMultiline();
1061- $data = explode("\r\n", $data);
1062- foreach ($data as $line) {
1063- sscanf($line, '%d %s', $msg_id, $uidl);
1064- $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);
1065- }
1066-
1067- return $return;
1068- }
1069- } else {
1070-
1071- $data = $this->_sendCmd('UIDL ' . $msg_id);
1072- sscanf($data, '+OK %d %s', $msg_id, $uidl);
1073- return array('msg_id' => $msg_id, 'uidl' => $uidl);
1074- }
1075- }
1076-
1077- return false;
1078- }
1079-
1080-
1081-
1082-
1083-
1084-
1085-
1086-
1087-
1088- /*
1089- * Sends a command, checks the reponse, and
1090- * if good returns the reponse, other wise
1091- * returns false.
1092- *
1093- * @param string $cmd Command to send (\r\n will be appended)
1094- * @return mixed First line of response if successful, otherwise false
1095- */
1096- function _sendCmd($cmd)
1097- {
1098- if (PEAR::isError($result = $this->_send($cmd) )){
1099- return $result ;
1100- }
1101-
1102- if (PEAR::isError($data = $this->_recvLn() )){
1103- return $data;
1104- }
1105-
1106- if ( strtoupper(substr($data, 0, 3)) == '+OK') {
1107- return $data;
1108- }
1109-
1110-
1111- return $this->_raiseError($data);
1112- }
1113-
1114- /*
1115- * Reads a multiline reponse and returns the data
1116- *
1117- * @return string The reponse.
1118- */
1119- function _getMultiline()
1120- {
1121- $data = '';
1122- while(!PEAR::isError($tmp = $this->_recvLn() ) ) {
1123- if($tmp == '.'){
1124- return substr($data, 0, -2);
1125- }
1126- if (substr($tmp, 0, 2) == '..') {
1127- $tmp = substr($tmp, 1);
1128- }
1129- $data .= $tmp . "\r\n";
1130- }
1131- return substr($data, 0, -2);
1132- }
1133-
1134-
1135- /**
1136- * Sets the bebug state
1137- *
1138- * @param bool $debug
1139- * @access public
1140- * @return void
1141- */
1142- function setDebug($debug=true)
1143- {
1144- $this->_debug=$debug;
1145- }
1146-
1147-
1148-
1149-
1150-
1151- /**
1152- * Send the given string of data to the server.
1153- *
1154- * @param string $data The string of data to send.
1155- *
1156- * @return mixed True on success or a PEAR_Error object on failure.
1157- *
1158- * @access private
1159- * @since 1.0
1160- */
1161- function _send($data)
1162- {
1163- if ($this->_debug) {
1164- echo "C: $data\n";
1165- }
1166-
1167- if (PEAR::isError($error = $this->_socket->writeLine($data))) {
1168- return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
1169- }
1170- return true;
1171- }
1172-
1173-
1174-
1175- /**
1176- * Receive the given string of data from the server.
1177- *
1178- * @return mixed a line of response on success or a PEAR_Error object on failure.
1179- *
1180- * @access private
1181- * @since 1.0
1182- */
1183- function _recvLn()
1184- {
1185- if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {
1186- return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );
1187- }
1188- if($this->_debug){
1189- // S: means this data was sent by the POP3 Server
1190- echo "S:$lastline\n" ;
1191- }
1192- return $lastline;
1193- }
1194-
1195- /**
1196- * Checks de server Response
1197- *
1198- * @param string $response the response
1199- * @return mixed true on success or a PEAR_Error object on failure.
1200- *
1201- * @access private
1202- * @since 1.3.3
1203- */
1204-
1205- function _checkResponse($response)
1206- {
1207- if (@substr(strtoupper($response), 0, 3) == '+OK') {
1208- return true;
1209- }else{
1210- if (@substr(strtoupper($response), 0, 4) == '-ERR') {
1211- return $this->_raiseError($response);
1212- }else{
1213- if (@substr(strtoupper($response), 0, 2) == '+ ') {
1214- return true;
1215- }
1216- }
1217-
1218- }
1219- return $this->_raiseError("Unknown Response ($response)");
1220- }
1221-
1222-
1223-
1224-}
1225-
1226-?>
1+<?php
2+// +-----------------------------------------------------------------------+
3+// | Copyright (c) 2002, Richard Heyes |
4+// | All rights reserved. |
5+// | |
6+// | Redistribution and use in source and binary forms, with or without |
7+// | modification, are permitted provided that the following conditions |
8+// | are met: |
9+// | |
10+// | o Redistributions of source code must retain the above copyright |
11+// | notice, this list of conditions and the following disclaimer. |
12+// | o Redistributions in binary form must reproduce the above copyright |
13+// | notice, this list of conditions and the following disclaimer in the |
14+// | documentation and/or other materials provided with the distribution.|
15+// | o The names of the authors may not be used to endorse or promote |
16+// | products derived from this software without specific prior written |
17+// | permission. |
18+// | |
19+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30+// | |
31+// +-----------------------------------------------------------------------+
32+// | Author: Richard Heyes <richard@phpguru.org> |
33+// | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar> |
34+// +-----------------------------------------------------------------------+
35+//
36+// $Id: POP3.php,v 1.7 2006/09/29 14:44:19 hsur Exp $
37+
38+require_once('Net/Socket.php');
39+
40+
41+
42+/**
43+* +----------------------------- IMPORTANT ------------------------------+
44+* | Usage of this class compared to native php extensions such as IMAP |
45+* | is slow and may be feature deficient. If available you are STRONGLY |
46+* | recommended to use the php extensions. |
47+* +----------------------------------------------------------------------+
48+*
49+* POP3 Access Class
50+*
51+* For usage see the example script
52+*/
53+
54+define('NET_POP3_STATE_DISCONNECTED', 1, true);
55+define('NET_POP3_STATE_AUTHORISATION', 2, true);
56+define('NET_POP3_STATE_TRANSACTION', 4, true);
57+
58+class Net_POP3 {
59+
60+ /*
61+ * Some basic information about the mail drop
62+ * garnered from the STAT command
63+ *
64+ * @var array
65+ */
66+ var $_maildrop;
67+
68+ /*
69+ * Used for APOP to store the timestamp
70+ *
71+ * @var string
72+ */
73+ var $_timestamp;
74+
75+ /*
76+ * Timeout that is passed to the socket object
77+ *
78+ * @var integer
79+ */
80+ var $_timeout;
81+
82+ /*
83+ * Socket object
84+ *
85+ * @var object
86+ */
87+ var $_socket;
88+
89+ /*
90+ * Current state of the connection. Used with the
91+ * constants defined above.
92+ *
93+ * @var integer
94+ */
95+ var $_state;
96+
97+ /*
98+ * Hostname to connect to
99+ *
100+ * @var string
101+ */
102+ var $_host;
103+
104+ /*
105+ * Port to connect to
106+ *
107+ * @var integer
108+ */
109+ var $_port;
110+
111+ /**
112+ * To allow class debuging
113+ * @var boolean
114+ */
115+ var $_debug = false;
116+
117+
118+ /**
119+ * The auth methods this class support
120+ * @var array
121+ */
122+ //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
123+ //Disabling DIGEST-MD5 for now
124+ var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
125+ //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
126+ //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
127+
128+
129+ /**
130+ * The auth methods this class support
131+ * @var array
132+ */
133+ var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
134+
135+
136+ /**
137+ * The capability response
138+ * @var array
139+ */
140+ var $_capability;
141+
142+ /*
143+ * Constructor. Sets up the object variables, and instantiates
144+ * the socket object.
145+ *
146+ */
147+
148+
149+ function Net_POP3()
150+ {
151+ $this->_timestamp = ''; // Used for APOP
152+ $this->_maildrop = array();
153+ $this->_timeout = 3;
154+ $this->_state = NET_POP3_STATE_DISCONNECTED;
155+ $this->_socket =& new Net_Socket();
156+ /*
157+ * Include the Auth_SASL package. If the package is not available,
158+ * we disable the authentication methods that depend upon it.
159+ */
160+ if ((@include_once 'Auth/SASL.php') == false) {
161+ if($this->_debug){
162+ echo "AUTH_SASL NOT PRESENT!\n";
163+ }
164+ foreach($this->supportedSASLAuthMethods as $SASLMethod){
165+ $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
166+ if($this->_debug){
167+ echo "DISABLING METHOD $SASLMethod\n";
168+ }
169+ unset($this->supportedAuthMethods[$pos]);
170+ }
171+ }
172+
173+
174+
175+ }
176+
177+
178+ /**
179+ * Handles the errors the class can find
180+ * on the server
181+ *
182+ * @access private
183+ * @return PEAR_Error
184+ */
185+
186+ function _raiseError($msg, $code =-1)
187+ {
188+ include_once 'PEAR.php';
189+ return PEAR::raiseError($msg, $code);
190+ }
191+
192+
193+
194+ /*
195+ * Connects to the given host on the given port.
196+ * Also looks for the timestamp in the greeting
197+ * needed for APOP authentication
198+ *
199+ * @param string $host Hostname/IP address to connect to
200+ * @param string $port Port to use to connect to on host
201+ * @return bool Success/Failure
202+ */
203+ function connect($host = 'localhost', $port = 110)
204+ {
205+ $this->_host = $host;
206+ $this->_port = $port;
207+
208+ $result = $this->_socket->connect($host, $port, false, $this->_timeout);
209+ if ($result === true) {
210+ $data = $this->_recvLn();
211+
212+ if( $this->_checkResponse($data) ){
213+ // if the response begins with '+OK' ...
214+// if (@substr(strtoupper($data), 0, 3) == '+OK') {
215+ // Check for string matching apop timestamp
216+ if (preg_match('/<.+@.+>/U', $data, $matches)) {
217+ $this->_timestamp = $matches[0];
218+ }
219+ $this->_maildrop = array();
220+ $this->_state = NET_POP3_STATE_AUTHORISATION;
221+
222+ return true;
223+ }
224+ }
225+
226+ $this->_socket->disconnect();
227+ return false;
228+ }
229+
230+ /*
231+ * Disconnect function. Sends the QUIT command
232+ * and closes the socket.
233+ *
234+ * @return bool Success/Failure
235+ */
236+ function disconnect()
237+ {
238+ return $this->_cmdQuit();
239+ }
240+
241+ /*
242+ * Performs the login procedure. If there is a timestamp
243+ * stored, APOP will be tried first, then basic USER/PASS.
244+ *
245+ * @param string $user Username to use
246+ * @param string $pass Password to use
247+ * @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");
248+ * Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER'
249+ * @return mixed true on Success/ PEAR_ERROR on error
250+ */
251+ function login($user, $pass, $apop = true)
252+ {
253+ if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
254+
255+ if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){
256+ return $ret;
257+ }
258+ if( ! PEAR::isError($ret)){
259+ $this->_state = NET_POP3_STATE_TRANSACTION;
260+ return true;
261+ }
262+
263+ }
264+ return $this->_raiseError('Generic login error' , 1);
265+ }
266+
267+
268+
269+ /**
270+ * Parses the response from the capability command. Stores
271+ * the result in $this->_capability
272+ *
273+ * @access private
274+ */
275+ function _parseCapability()
276+ {
277+
278+ if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){
279+ $data = $this->_getMultiline();
280+ }else {
281+ // CAPA command not supported, reset data var
282+ // to avoid Notice errors of preg_split on an object
283+ $data = '';
284+ }
285+ $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
286+
287+ for ($i = 0; $i < count($data); $i++) {
288+
289+ $capa='';
290+ if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
291+
292+ $capa=strtolower($matches[1]);
293+ switch ($capa) {
294+ case 'implementation':
295+ $this->_capability['implementation'] = $matches[3];
296+ break;
297+ case 'sasl':
298+ $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
299+ break;
300+ default :
301+ $this->_capability[$capa] = $matches[2];
302+ break;
303+ }
304+ }
305+ }
306+ }
307+
308+
309+
310+
311+ /**
312+ * Returns the name of the best authentication method that the server
313+ * has advertised.
314+ *
315+ * @param string if !=null,authenticate with this method ($userMethod).
316+ *
317+ * @return mixed Returns a string containing the name of the best
318+ * supported authentication method or a PEAR_Error object
319+ * if a failure condition is encountered.
320+ * @access private
321+ * @since 1.0
322+ */
323+ function _getBestAuthMethod($userMethod = null)
324+ {
325+
326+/*
327+ return 'USER';
328+ return 'APOP';
329+ return 'DIGEST-MD5';
330+ return 'CRAM-MD5';
331+*/
332+
333+
334+ $this->_parseCapability();
335+
336+ //unset($this->_capability['sasl']);
337+
338+ if( isset($this->_capability['sasl']) ){
339+ $serverMethods=$this->_capability['sasl'];
340+ }else{
341+ $serverMethods=array('USER');
342+ // Check for timestamp before attempting APOP
343+ if ($this->_timestamp != null)
344+ {
345+ $serverMethods[] = 'APOP';
346+ }
347+ }
348+
349+ if($userMethod !== null && $userMethod !== true ){
350+ $methods = array();
351+ $methods[] = $userMethod;
352+ return $userMethod;
353+ }else{
354+ $methods = $this->supportedAuthMethods;
355+ }
356+
357+ if( ($methods != null) && ($serverMethods != null)){
358+
359+ foreach ( $methods as $method ) {
360+
361+ if ( in_array( $method , $serverMethods ) ) {
362+ return $method;
363+ }
364+ }
365+ $serverMethods=implode(',' , $serverMethods );
366+ $myMethods=implode(',' ,$this->supportedAuthMethods);
367+ return $this->_raiseError("$method NOT supported authentication method!. This server " .
368+ "supports these methods: $serverMethods, but I support $myMethods");
369+ }else{
370+ return $this->_raiseError("This server don't support any Auth methods");
371+ }
372+ }
373+
374+
375+
376+
377+
378+
379+ /* Handles the authentication using any known method
380+ *
381+ * @param string The userid to authenticate as.
382+ * @param string The password to authenticate with.
383+ * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
384+ *
385+ * @return mixed string or PEAR_Error
386+ *
387+ * @access private
388+ * @since 1.0
389+ */
390+ function _cmdAuthenticate($uid , $pwd , $userMethod = null )
391+ {
392+
393+
394+ if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
395+ return $method;
396+ }
397+
398+ switch ($method) {
399+ case 'DIGEST-MD5':
400+ $result = $this->_authDigest_MD5( $uid , $pwd );
401+ break;
402+ case 'CRAM-MD5':
403+ $result = $this->_authCRAM_MD5( $uid , $pwd );
404+ break;
405+ case 'LOGIN':
406+ $result = $this->_authLOGIN( $uid , $pwd );
407+ break;
408+ case 'PLAIN':
409+ $result = $this->_authPLAIN( $uid , $pwd );
410+ break;
411+ case 'APOP':
412+ $result = $this->_cmdApop( $uid , $pwd );
413+ // if APOP fails fallback to USER auth
414+ if( PEAR::isError( $result ) ){
415+ //echo "APOP FAILED!!!\n";
416+ $result=$this->_authUSER( $uid , $pwd );
417+ }
418+ break;
419+ case 'USER':
420+ $result = $this->_authUSER( $uid , $pwd );
421+ break;
422+
423+
424+ default :
425+ $result = $this->_raiseError( "$method is not a supported authentication method" );
426+ break;
427+ }
428+ return $result;
429+ }
430+
431+
432+
433+
434+ /* Authenticates the user using the USER-PASS method.
435+ *
436+ * @param string The userid to authenticate as.
437+ * @param string The password to authenticate with.
438+ *
439+ * @return mixed true on success or PEAR_Error on failure
440+ *
441+ * @access private
442+ * @since 1.0
443+ */
444+ function _authUSER($user, $pass )
445+ {
446+ if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){
447+ return $ret;
448+ }
449+ if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){
450+ return $ret;
451+ }
452+ return true;
453+ }
454+
455+
456+
457+
458+
459+
460+
461+
462+ /* Authenticates the user using the PLAIN method.
463+ *
464+ * @param string The userid to authenticate as.
465+ * @param string The password to authenticate with.
466+ *
467+ * @return array Returns an array containing the response
468+ *
469+ * @access private
470+ * @since 1.0
471+ */
472+ function _authPLAIN($user, $pass )
473+ {
474+ $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );
475+
476+ if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {
477+ return $ret;
478+ }
479+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ){
480+ return $challenge;
481+ }
482+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
483+ return $ret;
484+ }
485+
486+ return true;
487+ }
488+
489+
490+
491+ /* Authenticates the user using the PLAIN method.
492+ *
493+ * @param string The userid to authenticate as.
494+ * @param string The password to authenticate with.
495+ *
496+ * @return array Returns an array containing the response
497+ *
498+ * @access private
499+ * @since 1.0
500+ */
501+ function _authLOGIN($user, $pass )
502+ {
503+ $this->_send('AUTH LOGIN');
504+
505+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
506+ return $challenge;
507+ }
508+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
509+ return $ret;
510+ }
511+
512+
513+ if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {
514+ return $ret;
515+ }
516+
517+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
518+ return $challenge;
519+ }
520+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
521+ return $ret;
522+ }
523+
524+ if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {
525+ return $ret;
526+ }
527+
528+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
529+ return $challenge;
530+ }
531+ return $this->_checkResponse($challenge);
532+ }
533+
534+
535+
536+
537+
538+ /* Authenticates the user using the CRAM-MD5 method.
539+ *
540+ * @param string The userid to authenticate as.
541+ * @param string The password to authenticate with.
542+ *
543+ * @return array Returns an array containing the response
544+ *
545+ * @access private
546+ * @since 1.0
547+ */
548+ function _authCRAM_MD5($uid, $pwd )
549+ {
550+ if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {
551+ return $ret;
552+ }
553+
554+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
555+ return $challenge;
556+ }
557+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
558+ return $ret;
559+ }
560+
561+ // remove '+ '
562+
563+ $challenge=substr($challenge,2);
564+
565+ $challenge = base64_decode( $challenge );
566+
567+ $cram = &Auth_SASL::factory('crammd5');
568+ $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
569+
570+
571+ if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
572+ return $error;
573+ }
574+ if ( PEAR::isError( $ret = $this->_recvLn() ) ) {
575+ return $ret;
576+ }
577+ //echo "RET:$ret\n";
578+ return $this->_checkResponse($ret);
579+ }
580+
581+
582+
583+ /* Authenticates the user using the DIGEST-MD5 method.
584+ *
585+ * @param string The userid to authenticate as.
586+ * @param string The password to authenticate with.
587+ * @param string The efective user
588+ *
589+ * @return array Returns an array containing the response
590+ *
591+ * @access private
592+ * @since 1.0
593+ */
594+ function _authDigest_MD5($uid, $pwd)
595+ {
596+ if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {
597+ return $ret;
598+ }
599+
600+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
601+ return $challenge;
602+ }
603+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
604+ return $ret;
605+ }
606+
607+ // remove '+ '
608+ $challenge=substr($challenge,2);
609+
610+ $challenge = base64_decode( $challenge );
611+ $digest = &Auth_SASL::factory('digestmd5');
612+ $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));
613+
614+ if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
615+ return $error;
616+ }
617+
618+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
619+ return $challenge;
620+ }
621+ if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
622+ return $ret;
623+ }
624+ /*
625+ * We don't use the protocol's third step because POP3 doesn't allow
626+ * subsequent authentication, so we just silently ignore it.
627+ */
628+
629+ if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {
630+ return $challenge ;
631+ }
632+
633+ if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
634+ return $challenge;
635+ }
636+
637+ return $this->_checkResponse($challenge);
638+
639+
640+ }
641+
642+
643+
644+
645+
646+
647+
648+
649+
650+
651+ /*
652+ * Sends the APOP command
653+ *
654+ * @param $user Username to send
655+ * @param $pass Password to send
656+ * @return bool Success/Failure
657+ */
658+ function _cmdApop($user, $pass)
659+ {
660+ if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
661+
662+ if (!empty($this->_timestamp)) {
663+ if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){
664+ return $data;
665+ }
666+ $this->_state = NET_POP3_STATE_TRANSACTION;
667+ return true;
668+ }
669+ }
670+ return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
671+ }
672+
673+
674+
675+
676+
677+
678+
679+
680+
681+
682+
683+
684+
685+
686+
687+ /*
688+ * Returns the raw headers of the specified message.
689+ *
690+ * @param integer $msg_id Message number
691+ * @return mixed Either raw headers or false on error
692+ */
693+ function getRawHeaders($msg_id)
694+ {
695+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
696+ return $this->_cmdTop($msg_id, 0);
697+ }
698+
699+ return false;
700+ }
701+
702+ /*
703+ * Returns the headers of the specified message in an
704+ * associative array. Array keys are the header names, array
705+ * values are the header values. In the case of multiple headers
706+ * having the same names, eg Received:, the array value will be
707+ * an indexed array of all the header values.
708+ *
709+ * @param integer $msg_id Message number
710+ * @return mixed Either array of headers or false on error
711+ */
712+ function getParsedHeaders($msg_id)
713+ {
714+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
715+
716+ $raw_headers = rtrim($this->getRawHeaders($msg_id));
717+
718+ $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
719+ $raw_headers = explode("\r\n", $raw_headers);
720+ foreach ($raw_headers as $value) {
721+ $name = substr($value, 0, $pos = strpos($value, ':'));
722+ $value = ltrim(substr($value, $pos + 1));
723+ if (isset($headers[$name]) AND is_array($headers[$name])) {
724+ $headers[$name][] = $value;
725+ } elseif (isset($headers[$name])) {
726+ $headers[$name] = array($headers[$name], $value);
727+ } else {
728+ $headers[$name] = $value;
729+ }
730+ }
731+
732+ return $headers;
733+ }
734+
735+ return false;
736+ }
737+
738+ /*
739+ * Returns the body of the message with given message number.
740+ *
741+ * @param integer $msg_id Message number
742+ * @return mixed Either message body or false on error
743+ */
744+ function getBody($msg_id)
745+ {
746+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
747+ $msg = $this->_cmdRetr($msg_id);
748+ return substr($msg, strpos($msg, "\r\n\r\n")+4);
749+ }
750+
751+ return false;
752+ }
753+
754+ /*
755+ * Returns the entire message with given message number.
756+ *
757+ * @param integer $msg_id Message number
758+ * @return mixed Either entire message or false on error
759+ */
760+ function getMsg($msg_id)
761+ {
762+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
763+ return $this->_cmdRetr($msg_id);
764+ }
765+
766+ return false;
767+ }
768+
769+ /*
770+ * Returns the size of the maildrop
771+ *
772+ * @return mixed Either size of maildrop or false on error
773+ */
774+ function getSize()
775+ {
776+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
777+ if (isset($this->_maildrop['size'])) {
778+ return $this->_maildrop['size'];
779+ } else {
780+ list(, $size) = $this->_cmdStat();
781+ return $size;
782+ }
783+ }
784+
785+ return false;
786+ }
787+
788+ /*
789+ * Returns number of messages in this maildrop
790+ *
791+ * @return mixed Either number of messages or false on error
792+ */
793+ function numMsg()
794+ {
795+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
796+ if (isset($this->_maildrop['num_msg'])) {
797+ return $this->_maildrop['num_msg'];
798+ } else {
799+ list($num_msg, ) = $this->_cmdStat();
800+ return $num_msg;
801+ }
802+ }
803+
804+ return false;
805+ }
806+
807+ /*
808+ * Marks a message for deletion. Only will be deleted if the
809+ * disconnect() method is called.
810+ *
811+ * @param integer $msg_id Message to delete
812+ * @return bool Success/Failure
813+ */
814+ function deleteMsg($msg_id)
815+ {
816+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
817+ return $this->_cmdDele($msg_id);
818+ }
819+
820+ return false;
821+ }
822+
823+ /*
824+ * Combination of LIST/UIDL commands, returns an array
825+ * of data
826+ *
827+ * @param integer $msg_id Optional message number
828+ * @return mixed Array of data or false on error
829+ */
830+ function getListing($msg_id = null)
831+ {
832+
833+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
834+ if (!isset($msg_id)){
835+
836+ $list=array();
837+ if ($list = $this->_cmdList()) {
838+ if ($uidl = $this->_cmdUidl()) {
839+ foreach ($uidl as $i => $value) {
840+ $list[$i]['uidl'] = $value['uidl'];
841+ }
842+ }
843+ return $list;
844+ }else{
845+ return array();
846+ }
847+ } else {
848+ if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {
849+ return array_merge($list, $uidl);
850+ }
851+ }
852+ }
853+
854+ return false;
855+ }
856+
857+ /*
858+ * Sends the USER command
859+ *
860+ * @param string $user Username to send
861+ * @return bool Success/Failure
862+ */
863+ function _cmdUser($user)
864+ {
865+ if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
866+ return $this->_sendCmd('USER ' . $user);
867+ }
868+ return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
869+ }
870+
871+
872+ /*
873+ * Sends the PASS command
874+ *
875+ * @param string $pass Password to send
876+ * @return bool Success/Failure
877+ */
878+ function _cmdPass($pass)
879+ {
880+ if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
881+ return $this->_sendCmd('PASS ' . $pass);
882+ }
883+ return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
884+ }
885+
886+
887+ /*
888+ * Sends the STAT command
889+ *
890+ * @return mixed Indexed array of number of messages and
891+ * maildrop size, or false on error.
892+ */
893+ function _cmdStat()
894+ {
895+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
896+ if(!PEAR::isError($data = $this->_sendCmd('STAT'))){
897+ sscanf($data, '+OK %d %d', $msg_num, $size);
898+ $this->_maildrop['num_msg'] = $msg_num;
899+ $this->_maildrop['size'] = $size;
900+
901+ return array($msg_num, $size);
902+ }
903+ }
904+ return false;
905+ }
906+
907+
908+ /*
909+ * Sends the LIST command
910+ *
911+ * @param integer $msg_id Optional message number
912+ * @return mixed Indexed array of msg_id/msg size or
913+ * false on error
914+ */
915+ function _cmdList($msg_id = null)
916+ {
917+ $return=array();
918+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
919+ if (!isset($msg_id)) {
920+ if(!PEAR::isError($data = $this->_sendCmd('LIST') )){
921+ $data = $this->_getMultiline();
922+ $data = explode("\r\n", $data);
923+ foreach ($data as $line) {
924+ if($line !=''){
925+ sscanf($line, '%s %s', $msg_id, $size);
926+ $return[] = array('msg_id' => $msg_id, 'size' => $size);
927+ }
928+ }
929+ return $return;
930+ }
931+ } else {
932+ if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){
933+ if($data!=''){
934+ sscanf($data, '+OK %d %d', $msg_id, $size);
935+ return array('msg_id' => $msg_id, 'size' => $size);
936+ }
937+ return array();
938+ }
939+ }
940+ }
941+
942+
943+ return false;
944+ }
945+
946+
947+ /*
948+ * Sends the RETR command
949+ *
950+ * @param integer $msg_id The message number to retrieve
951+ * @return mixed The message or false on error
952+ */
953+ function _cmdRetr($msg_id)
954+ {
955+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
956+ if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){
957+ $data = $this->_getMultiline();
958+ return $data;
959+ }
960+ }
961+
962+ return false;
963+ }
964+
965+
966+ /*
967+ * Sends the DELE command
968+ *
969+ * @param integer $msg_id Message number to mark as deleted
970+ * @return bool Success/Failure
971+ */
972+ function _cmdDele($msg_id)
973+ {
974+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
975+ return $this->_sendCmd('DELE ' . $msg_id);
976+ }
977+
978+ return false;
979+ }
980+
981+
982+ /*
983+ * Sends the NOOP command
984+ *
985+ * @return bool Success/Failure
986+ */
987+ function _cmdNoop()
988+ {
989+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
990+ if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){
991+ return true;
992+ }
993+ }
994+
995+ return false;
996+ }
997+
998+ /*
999+ * Sends the RSET command
1000+ *
1001+ * @return bool Success/Failure
1002+ */
1003+ function _cmdRset()
1004+ {
1005+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1006+ if(!PEAR::isError($data = $this->_sendCmd('RSET'))){
1007+ return true;
1008+ }
1009+ }
1010+
1011+ return false;
1012+ }
1013+
1014+ /*
1015+ * Sends the QUIT command
1016+ *
1017+ * @return bool Success/Failure
1018+ */
1019+ function _cmdQuit()
1020+ {
1021+ $data = $this->_sendCmd('QUIT');
1022+ $this->_state = NET_POP3_STATE_DISCONNECTED;
1023+ $this->_socket->disconnect();
1024+
1025+ return (bool)$data;
1026+ }
1027+
1028+
1029+ /*
1030+ * Sends the TOP command
1031+ *
1032+ * @param integer $msg_id Message number
1033+ * @param integer $num_lines Number of lines to retrieve
1034+ * @return mixed Message data or false on error
1035+ */
1036+ function _cmdTop($msg_id, $num_lines)
1037+ {
1038+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1039+
1040+ if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){
1041+ return $this->_getMultiline();
1042+ }
1043+ }
1044+
1045+ return false;
1046+ }
1047+
1048+ /*
1049+ * Sends the UIDL command
1050+ *
1051+ * @param integer $msg_id Message number
1052+ * @return mixed indexed array of msg_id/uidl or false on error
1053+ */
1054+ function _cmdUidl($msg_id = null)
1055+ {
1056+ if ($this->_state == NET_POP3_STATE_TRANSACTION) {
1057+
1058+ if (!isset($msg_id)) {
1059+ if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){
1060+ $data = $this->_getMultiline();
1061+ $data = explode("\r\n", $data);
1062+ foreach ($data as $line) {
1063+ sscanf($line, '%d %s', $msg_id, $uidl);
1064+ $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);
1065+ }
1066+
1067+ return $return;
1068+ }
1069+ } else {
1070+
1071+ $data = $this->_sendCmd('UIDL ' . $msg_id);
1072+ sscanf($data, '+OK %d %s', $msg_id, $uidl);
1073+ return array('msg_id' => $msg_id, 'uidl' => $uidl);
1074+ }
1075+ }
1076+
1077+ return false;
1078+ }
1079+
1080+
1081+
1082+
1083+
1084+
1085+
1086+
1087+
1088+ /*
1089+ * Sends a command, checks the reponse, and
1090+ * if good returns the reponse, other wise
1091+ * returns false.
1092+ *
1093+ * @param string $cmd Command to send (\r\n will be appended)
1094+ * @return mixed First line of response if successful, otherwise false
1095+ */
1096+ function _sendCmd($cmd)
1097+ {
1098+ if (PEAR::isError($result = $this->_send($cmd) )){
1099+ return $result ;
1100+ }
1101+
1102+ if (PEAR::isError($data = $this->_recvLn() )){
1103+ return $data;
1104+ }
1105+
1106+ if ( strtoupper(substr($data, 0, 3)) == '+OK') {
1107+ return $data;
1108+ }
1109+
1110+
1111+ return $this->_raiseError($data);
1112+ }
1113+
1114+ /*
1115+ * Reads a multiline reponse and returns the data
1116+ *
1117+ * @return string The reponse.
1118+ */
1119+ function _getMultiline()
1120+ {
1121+ $data = '';
1122+ while(!PEAR::isError($tmp = $this->_recvLn() ) ) {
1123+ if($tmp == '.'){
1124+ return substr($data, 0, -2);
1125+ }
1126+ if (substr($tmp, 0, 2) == '..') {
1127+ $tmp = substr($tmp, 1);
1128+ }
1129+ $data .= $tmp . "\r\n";
1130+ }
1131+ return substr($data, 0, -2);
1132+ }
1133+
1134+
1135+ /**
1136+ * Sets the bebug state
1137+ *
1138+ * @param bool $debug
1139+ * @access public
1140+ * @return void
1141+ */
1142+ function setDebug($debug=true)
1143+ {
1144+ $this->_debug=$debug;
1145+ }
1146+
1147+
1148+
1149+
1150+
1151+ /**
1152+ * Send the given string of data to the server.
1153+ *
1154+ * @param string $data The string of data to send.
1155+ *
1156+ * @return mixed True on success or a PEAR_Error object on failure.
1157+ *
1158+ * @access private
1159+ * @since 1.0
1160+ */
1161+ function _send($data)
1162+ {
1163+ if ($this->_debug) {
1164+ echo "C: $data\n";
1165+ }
1166+
1167+ if (PEAR::isError($error = $this->_socket->writeLine($data))) {
1168+ return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
1169+ }
1170+ return true;
1171+ }
1172+
1173+
1174+
1175+ /**
1176+ * Receive the given string of data from the server.
1177+ *
1178+ * @return mixed a line of response on success or a PEAR_Error object on failure.
1179+ *
1180+ * @access private
1181+ * @since 1.0
1182+ */
1183+ function _recvLn()
1184+ {
1185+ if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {
1186+ return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );
1187+ }
1188+ if($this->_debug){
1189+ // S: means this data was sent by the POP3 Server
1190+ echo "S:$lastline\n" ;
1191+ }
1192+ return $lastline;
1193+ }
1194+
1195+ /**
1196+ * Checks de server Response
1197+ *
1198+ * @param string $response the response
1199+ * @return mixed true on success or a PEAR_Error object on failure.
1200+ *
1201+ * @access private
1202+ * @since 1.3.3
1203+ */
1204+
1205+ function _checkResponse($response)
1206+ {
1207+ if (@substr(strtoupper($response), 0, 3) == '+OK') {
1208+ return true;
1209+ }else{
1210+ if (@substr(strtoupper($response), 0, 4) == '-ERR') {
1211+ return $this->_raiseError($response);
1212+ }else{
1213+ if (@substr(strtoupper($response), 0, 2) == '+ ') {
1214+ return true;
1215+ }
1216+ }
1217+
1218+ }
1219+ return $this->_raiseError("Unknown Response ($response)");
1220+ }
1221+
1222+
1223+
1224+}
1225+
1226+?>
--- a/trunk/NP_Moblog/sharedlibs/Net/Socket.php
+++ b/trunk/NP_Moblog/sharedlibs/Net/Socket.php
@@ -1,528 +1,528 @@
1-<?php
2-//
3-// +----------------------------------------------------------------------+
4-// | PHP Version 4 |
5-// +----------------------------------------------------------------------+
6-// | Copyright (c) 1997-2003 The PHP Group |
7-// +----------------------------------------------------------------------+
8-// | This source file is subject to version 2.0 of the PHP license, |
9-// | that is bundled with this package in the file LICENSE, and is |
10-// | available at through the world-wide-web at |
11-// | http://www.php.net/license/2_02.txt. |
12-// | If you did not receive a copy of the PHP license and are unable to |
13-// | obtain it through the world-wide-web, please send a note to |
14-// | license@php.net so we can mail you a copy immediately. |
15-// +----------------------------------------------------------------------+
16-// | Authors: Stig Bakken <ssb@php.net> |
17-// | Chuck Hagenbuch <chuck@horde.org> |
18-// +----------------------------------------------------------------------+
19-//
20-// $Id: Socket.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
21-
22-require_once 'PEAR.php';
23-
24-define('NET_SOCKET_READ', 1);
25-define('NET_SOCKET_WRITE', 2);
26-define('NET_SOCKET_ERROR', 3);
27-
28-/**
29- * Generalized Socket class.
30- *
31- * @version 1.1
32- * @author Stig Bakken <ssb@php.net>
33- * @author Chuck Hagenbuch <chuck@horde.org>
34- */
35-class Net_Socket extends PEAR {
36-
37- /**
38- * Socket file pointer.
39- * @var resource $fp
40- */
41- var $fp = null;
42-
43- /**
44- * Whether the socket is blocking. Defaults to true.
45- * @var boolean $blocking
46- */
47- var $blocking = true;
48-
49- /**
50- * Whether the socket is persistent. Defaults to false.
51- * @var boolean $persistent
52- */
53- var $persistent = false;
54-
55- /**
56- * The IP address to connect to.
57- * @var string $addr
58- */
59- var $addr = '';
60-
61- /**
62- * The port number to connect to.
63- * @var integer $port
64- */
65- var $port = 0;
66-
67- /**
68- * Number of seconds to wait on socket connections before assuming
69- * there's no more data. Defaults to no timeout.
70- * @var integer $timeout
71- */
72- var $timeout = false;
73-
74- /**
75- * Number of bytes to read at a time in readLine() and
76- * readAll(). Defaults to 2048.
77- * @var integer $lineLength
78- */
79- var $lineLength = 2048;
80-
81- /**
82- * Connect to the specified port. If called when the socket is
83- * already connected, it disconnects and connects again.
84- *
85- * @param string $addr IP address or host name.
86- * @param integer $port TCP port number.
87- * @param boolean $persistent (optional) Whether the connection is
88- * persistent (kept open between requests
89- * by the web server).
90- * @param integer $timeout (optional) How long to wait for data.
91- * @param array $options See options for stream_context_create.
92- *
93- * @access public
94- *
95- * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
96- */
97- function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
98- {
99- if (is_resource($this->fp)) {
100- @fclose($this->fp);
101- $this->fp = null;
102- }
103-
104- if (!$addr) {
105- return $this->raiseError('$addr cannot be empty');
106- } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
107- strstr($addr, '/') !== false) {
108- $this->addr = $addr;
109- } else {
110- $this->addr = @gethostbyname($addr);
111- }
112-
113- $this->port = $port % 65536;
114-
115- if ($persistent !== null) {
116- $this->persistent = $persistent;
117- }
118-
119- if ($timeout !== null) {
120- $this->timeout = $timeout;
121- }
122-
123- $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
124- $errno = 0;
125- $errstr = '';
126- if ($options && function_exists('stream_context_create')) {
127- if ($this->timeout) {
128- $timeout = $this->timeout;
129- } else {
130- $timeout = 0;
131- }
132- $context = stream_context_create($options);
133- $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
134- } else {
135- if ($this->timeout) {
136- $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
137- } else {
138- $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
139- }
140- }
141-
142- if (!$fp) {
143- return $this->raiseError($errstr, $errno);
144- }
145-
146- $this->fp = $fp;
147-
148- return $this->setBlocking($this->blocking);
149- }
150-
151- /**
152- * Disconnects from the peer, closes the socket.
153- *
154- * @access public
155- * @return mixed true on success or an error object otherwise
156- */
157- function disconnect()
158- {
159- if (!is_resource($this->fp)) {
160- return $this->raiseError('not connected');
161- }
162-
163- @fclose($this->fp);
164- $this->fp = null;
165- return true;
166- }
167-
168- /**
169- * Find out if the socket is in blocking mode.
170- *
171- * @access public
172- * @return boolean The current blocking mode.
173- */
174- function isBlocking()
175- {
176- return $this->blocking;
177- }
178-
179- /**
180- * Sets whether the socket connection should be blocking or
181- * not. A read call to a non-blocking socket will return immediately
182- * if there is no data available, whereas it will block until there
183- * is data for blocking sockets.
184- *
185- * @param boolean $mode True for blocking sockets, false for nonblocking.
186- * @access public
187- * @return mixed true on success or an error object otherwise
188- */
189- function setBlocking($mode)
190- {
191- if (!is_resource($this->fp)) {
192- return $this->raiseError('not connected');
193- }
194-
195- $this->blocking = $mode;
196- socket_set_blocking($this->fp, $this->blocking);
197- return true;
198- }
199-
200- /**
201- * Sets the timeout value on socket descriptor,
202- * expressed in the sum of seconds and microseconds
203- *
204- * @param integer $seconds Seconds.
205- * @param integer $microseconds Microseconds.
206- * @access public
207- * @return mixed true on success or an error object otherwise
208- */
209- function setTimeout($seconds, $microseconds)
210- {
211- if (!is_resource($this->fp)) {
212- return $this->raiseError('not connected');
213- }
214-
215- return socket_set_timeout($this->fp, $seconds, $microseconds);
216- }
217-
218- /**
219- * Returns information about an existing socket resource.
220- * Currently returns four entries in the result array:
221- *
222- * <p>
223- * timed_out (bool) - The socket timed out waiting for data<br>
224- * blocked (bool) - The socket was blocked<br>
225- * eof (bool) - Indicates EOF event<br>
226- * unread_bytes (int) - Number of bytes left in the socket buffer<br>
227- * </p>
228- *
229- * @access public
230- * @return mixed Array containing information about existing socket resource or an error object otherwise
231- */
232- function getStatus()
233- {
234- if (!is_resource($this->fp)) {
235- return $this->raiseError('not connected');
236- }
237-
238- return socket_get_status($this->fp);
239- }
240-
241- /**
242- * Get a specified line of data
243- *
244- * @access public
245- * @return $size bytes of data from the socket, or a PEAR_Error if
246- * not connected.
247- */
248- function gets($size)
249- {
250- if (!is_resource($this->fp)) {
251- return $this->raiseError('not connected');
252- }
253-
254- return @fgets($this->fp, $size);
255- }
256-
257- /**
258- * Read a specified amount of data. This is guaranteed to return,
259- * and has the added benefit of getting everything in one fread()
260- * chunk; if you know the size of the data you're getting
261- * beforehand, this is definitely the way to go.
262- *
263- * @param integer $size The number of bytes to read from the socket.
264- * @access public
265- * @return $size bytes of data from the socket, or a PEAR_Error if
266- * not connected.
267- */
268- function read($size)
269- {
270- if (!is_resource($this->fp)) {
271- return $this->raiseError('not connected');
272- }
273-
274- return @fread($this->fp, $size);
275- }
276-
277- /**
278- * Write a specified amount of data.
279- *
280- * @param string $data Data to write.
281- * @param integer $blocksize Amount of data to write at once.
282- * NULL means all at once.
283- *
284- * @access public
285- * @return mixed true on success or an error object otherwise
286- */
287- function write($data, $blocksize = null)
288- {
289- if (!is_resource($this->fp)) {
290- return $this->raiseError('not connected');
291- }
292-
293- if (is_null($blocksize) && !OS_WINDOWS) {
294- return fwrite($this->fp, $data);
295- } else {
296- if (is_null($blocksize)) {
297- $blocksize = 1024;
298- }
299-
300- $pos = 0;
301- $size = strlen($data);
302- while ($pos < $size) {
303- $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
304- if ($written === false) {
305- return false;
306- }
307- $pos += $written;
308- }
309-
310- return $pos;
311- }
312- }
313-
314- /**
315- * Write a line of data to the socket, followed by a trailing "\r\n".
316- *
317- * @access public
318- * @return mixed fputs result, or an error
319- */
320- function writeLine($data)
321- {
322- if (!is_resource($this->fp)) {
323- return $this->raiseError('not connected');
324- }
325-
326- return fwrite($this->fp, $data . "\r\n");
327- }
328-
329- /**
330- * Tests for end-of-file on a socket descriptor.
331- *
332- * @access public
333- * @return bool
334- */
335- function eof()
336- {
337- return (is_resource($this->fp) && feof($this->fp));
338- }
339-
340- /**
341- * Reads a byte of data
342- *
343- * @access public
344- * @return 1 byte of data from the socket, or a PEAR_Error if
345- * not connected.
346- */
347- function readByte()
348- {
349- if (!is_resource($this->fp)) {
350- return $this->raiseError('not connected');
351- }
352-
353- return ord(@fread($this->fp, 1));
354- }
355-
356- /**
357- * Reads a word of data
358- *
359- * @access public
360- * @return 1 word of data from the socket, or a PEAR_Error if
361- * not connected.
362- */
363- function readWord()
364- {
365- if (!is_resource($this->fp)) {
366- return $this->raiseError('not connected');
367- }
368-
369- $buf = @fread($this->fp, 2);
370- return (ord($buf[0]) + (ord($buf[1]) << 8));
371- }
372-
373- /**
374- * Reads an int of data
375- *
376- * @access public
377- * @return integer 1 int of data from the socket, or a PEAR_Error if
378- * not connected.
379- */
380- function readInt()
381- {
382- if (!is_resource($this->fp)) {
383- return $this->raiseError('not connected');
384- }
385-
386- $buf = @fread($this->fp, 4);
387- return (ord($buf[0]) + (ord($buf[1]) << 8) +
388- (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
389- }
390-
391- /**
392- * Reads a zero-terminated string of data
393- *
394- * @access public
395- * @return string, or a PEAR_Error if
396- * not connected.
397- */
398- function readString()
399- {
400- if (!is_resource($this->fp)) {
401- return $this->raiseError('not connected');
402- }
403-
404- $string = '';
405- while (($char = @fread($this->fp, 1)) != "\x00") {
406- $string .= $char;
407- }
408- return $string;
409- }
410-
411- /**
412- * Reads an IP Address and returns it in a dot formated string
413- *
414- * @access public
415- * @return Dot formated string, or a PEAR_Error if
416- * not connected.
417- */
418- function readIPAddress()
419- {
420- if (!is_resource($this->fp)) {
421- return $this->raiseError('not connected');
422- }
423-
424- $buf = @fread($this->fp, 4);
425- return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
426- ord($buf[2]), ord($buf[3]));
427- }
428-
429- /**
430- * Read until either the end of the socket or a newline, whichever
431- * comes first. Strips the trailing newline from the returned data.
432- *
433- * @access public
434- * @return All available data up to a newline, without that
435- * newline, or until the end of the socket, or a PEAR_Error if
436- * not connected.
437- */
438- function readLine()
439- {
440- if (!is_resource($this->fp)) {
441- return $this->raiseError('not connected');
442- }
443-
444- $line = '';
445- $timeout = time() + $this->timeout;
446- while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
447- $line .= @fgets($this->fp, $this->lineLength);
448- if (substr($line, -1) == "\n") {
449- return rtrim($line, "\r\n");
450- }
451- }
452- return $line;
453- }
454-
455- /**
456- * Read until the socket closes, or until there is no more data in
457- * the inner PHP buffer. If the inner buffer is empty, in blocking
458- * mode we wait for at least 1 byte of data. Therefore, in
459- * blocking mode, if there is no data at all to be read, this
460- * function will never exit (unless the socket is closed on the
461- * remote end).
462- *
463- * @access public
464- *
465- * @return string All data until the socket closes, or a PEAR_Error if
466- * not connected.
467- */
468- function readAll()
469- {
470- if (!is_resource($this->fp)) {
471- return $this->raiseError('not connected');
472- }
473-
474- $data = '';
475- while (!feof($this->fp)) {
476- $data .= @fread($this->fp, $this->lineLength);
477- }
478- return $data;
479- }
480-
481- /**
482- * Runs the equivalent of the select() system call on the socket
483- * with a timeout specified by tv_sec and tv_usec.
484- *
485- * @param integer $state Which of read/write/error to check for.
486- * @param integer $tv_sec Number of seconds for timeout.
487- * @param integer $tv_usec Number of microseconds for timeout.
488- *
489- * @access public
490- * @return False if select fails, integer describing which of read/write/error
491- * are ready, or PEAR_Error if not connected.
492- */
493- function select($state, $tv_sec, $tv_usec = 0)
494- {
495- if (!is_resource($this->fp)) {
496- return $this->raiseError('not connected');
497- }
498-
499- $read = null;
500- $write = null;
501- $except = null;
502- if ($state & NET_SOCKET_READ) {
503- $read[] = $this->fp;
504- }
505- if ($state & NET_SOCKET_WRITE) {
506- $write[] = $this->fp;
507- }
508- if ($state & NET_SOCKET_ERROR) {
509- $except[] = $this->fp;
510- }
511- if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
512- return false;
513- }
514-
515- $result = 0;
516- if (count($read)) {
517- $result |= NET_SOCKET_READ;
518- }
519- if (count($write)) {
520- $result |= NET_SOCKET_WRITE;
521- }
522- if (count($except)) {
523- $result |= NET_SOCKET_ERROR;
524- }
525- return $result;
526- }
527-
528-}
1+<?php
2+//
3+// +----------------------------------------------------------------------+
4+// | PHP Version 4 |
5+// +----------------------------------------------------------------------+
6+// | Copyright (c) 1997-2003 The PHP Group |
7+// +----------------------------------------------------------------------+
8+// | This source file is subject to version 2.0 of the PHP license, |
9+// | that is bundled with this package in the file LICENSE, and is |
10+// | available at through the world-wide-web at |
11+// | http://www.php.net/license/2_02.txt. |
12+// | If you did not receive a copy of the PHP license and are unable to |
13+// | obtain it through the world-wide-web, please send a note to |
14+// | license@php.net so we can mail you a copy immediately. |
15+// +----------------------------------------------------------------------+
16+// | Authors: Stig Bakken <ssb@php.net> |
17+// | Chuck Hagenbuch <chuck@horde.org> |
18+// +----------------------------------------------------------------------+
19+//
20+// $Id: Socket.php,v 1.8 2006/11/27 17:14:39 hsur Exp $
21+
22+require_once 'PEAR.php';
23+
24+define('NET_SOCKET_READ', 1);
25+define('NET_SOCKET_WRITE', 2);
26+define('NET_SOCKET_ERROR', 3);
27+
28+/**
29+ * Generalized Socket class.
30+ *
31+ * @version 1.1
32+ * @author Stig Bakken <ssb@php.net>
33+ * @author Chuck Hagenbuch <chuck@horde.org>
34+ */
35+class Net_Socket extends PEAR {
36+
37+ /**
38+ * Socket file pointer.
39+ * @var resource $fp
40+ */
41+ var $fp = null;
42+
43+ /**
44+ * Whether the socket is blocking. Defaults to true.
45+ * @var boolean $blocking
46+ */
47+ var $blocking = true;
48+
49+ /**
50+ * Whether the socket is persistent. Defaults to false.
51+ * @var boolean $persistent
52+ */
53+ var $persistent = false;
54+
55+ /**
56+ * The IP address to connect to.
57+ * @var string $addr
58+ */
59+ var $addr = '';
60+
61+ /**
62+ * The port number to connect to.
63+ * @var integer $port
64+ */
65+ var $port = 0;
66+
67+ /**
68+ * Number of seconds to wait on socket connections before assuming
69+ * there's no more data. Defaults to no timeout.
70+ * @var integer $timeout
71+ */
72+ var $timeout = false;
73+
74+ /**
75+ * Number of bytes to read at a time in readLine() and
76+ * readAll(). Defaults to 2048.
77+ * @var integer $lineLength
78+ */
79+ var $lineLength = 2048;
80+
81+ /**
82+ * Connect to the specified port. If called when the socket is
83+ * already connected, it disconnects and connects again.
84+ *
85+ * @param string $addr IP address or host name.
86+ * @param integer $port TCP port number.
87+ * @param boolean $persistent (optional) Whether the connection is
88+ * persistent (kept open between requests
89+ * by the web server).
90+ * @param integer $timeout (optional) How long to wait for data.
91+ * @param array $options See options for stream_context_create.
92+ *
93+ * @access public
94+ *
95+ * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
96+ */
97+ function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
98+ {
99+ if (is_resource($this->fp)) {
100+ @fclose($this->fp);
101+ $this->fp = null;
102+ }
103+
104+ if (!$addr) {
105+ return $this->raiseError('$addr cannot be empty');
106+ } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
107+ strstr($addr, '/') !== false) {
108+ $this->addr = $addr;
109+ } else {
110+ $this->addr = @gethostbyname($addr);
111+ }
112+
113+ $this->port = $port % 65536;
114+
115+ if ($persistent !== null) {
116+ $this->persistent = $persistent;
117+ }
118+
119+ if ($timeout !== null) {
120+ $this->timeout = $timeout;
121+ }
122+
123+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
124+ $errno = 0;
125+ $errstr = '';
126+ if ($options && function_exists('stream_context_create')) {
127+ if ($this->timeout) {
128+ $timeout = $this->timeout;
129+ } else {
130+ $timeout = 0;
131+ }
132+ $context = stream_context_create($options);
133+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
134+ } else {
135+ if ($this->timeout) {
136+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
137+ } else {
138+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
139+ }
140+ }
141+
142+ if (!$fp) {
143+ return $this->raiseError($errstr, $errno);
144+ }
145+
146+ $this->fp = $fp;
147+
148+ return $this->setBlocking($this->blocking);
149+ }
150+
151+ /**
152+ * Disconnects from the peer, closes the socket.
153+ *
154+ * @access public
155+ * @return mixed true on success or an error object otherwise
156+ */
157+ function disconnect()
158+ {
159+ if (!is_resource($this->fp)) {
160+ return $this->raiseError('not connected');
161+ }
162+
163+ @fclose($this->fp);
164+ $this->fp = null;
165+ return true;
166+ }
167+
168+ /**
169+ * Find out if the socket is in blocking mode.
170+ *
171+ * @access public
172+ * @return boolean The current blocking mode.
173+ */
174+ function isBlocking()
175+ {
176+ return $this->blocking;
177+ }
178+
179+ /**
180+ * Sets whether the socket connection should be blocking or
181+ * not. A read call to a non-blocking socket will return immediately
182+ * if there is no data available, whereas it will block until there
183+ * is data for blocking sockets.
184+ *
185+ * @param boolean $mode True for blocking sockets, false for nonblocking.
186+ * @access public
187+ * @return mixed true on success or an error object otherwise
188+ */
189+ function setBlocking($mode)
190+ {
191+ if (!is_resource($this->fp)) {
192+ return $this->raiseError('not connected');
193+ }
194+
195+ $this->blocking = $mode;
196+ socket_set_blocking($this->fp, $this->blocking);
197+ return true;
198+ }
199+
200+ /**
201+ * Sets the timeout value on socket descriptor,
202+ * expressed in the sum of seconds and microseconds
203+ *
204+ * @param integer $seconds Seconds.
205+ * @param integer $microseconds Microseconds.
206+ * @access public
207+ * @return mixed true on success or an error object otherwise
208+ */
209+ function setTimeout($seconds, $microseconds)
210+ {
211+ if (!is_resource($this->fp)) {
212+ return $this->raiseError('not connected');
213+ }
214+
215+ return socket_set_timeout($this->fp, $seconds, $microseconds);
216+ }
217+
218+ /**
219+ * Returns information about an existing socket resource.
220+ * Currently returns four entries in the result array:
221+ *
222+ * <p>
223+ * timed_out (bool) - The socket timed out waiting for data<br>
224+ * blocked (bool) - The socket was blocked<br>
225+ * eof (bool) - Indicates EOF event<br>
226+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
227+ * </p>
228+ *
229+ * @access public
230+ * @return mixed Array containing information about existing socket resource or an error object otherwise
231+ */
232+ function getStatus()
233+ {
234+ if (!is_resource($this->fp)) {
235+ return $this->raiseError('not connected');
236+ }
237+
238+ return socket_get_status($this->fp);
239+ }
240+
241+ /**
242+ * Get a specified line of data
243+ *
244+ * @access public
245+ * @return $size bytes of data from the socket, or a PEAR_Error if
246+ * not connected.
247+ */
248+ function gets($size)
249+ {
250+ if (!is_resource($this->fp)) {
251+ return $this->raiseError('not connected');
252+ }
253+
254+ return @fgets($this->fp, $size);
255+ }
256+
257+ /**
258+ * Read a specified amount of data. This is guaranteed to return,
259+ * and has the added benefit of getting everything in one fread()
260+ * chunk; if you know the size of the data you're getting
261+ * beforehand, this is definitely the way to go.
262+ *
263+ * @param integer $size The number of bytes to read from the socket.
264+ * @access public
265+ * @return $size bytes of data from the socket, or a PEAR_Error if
266+ * not connected.
267+ */
268+ function read($size)
269+ {
270+ if (!is_resource($this->fp)) {
271+ return $this->raiseError('not connected');
272+ }
273+
274+ return @fread($this->fp, $size);
275+ }
276+
277+ /**
278+ * Write a specified amount of data.
279+ *
280+ * @param string $data Data to write.
281+ * @param integer $blocksize Amount of data to write at once.
282+ * NULL means all at once.
283+ *
284+ * @access public
285+ * @return mixed true on success or an error object otherwise
286+ */
287+ function write($data, $blocksize = null)
288+ {
289+ if (!is_resource($this->fp)) {
290+ return $this->raiseError('not connected');
291+ }
292+
293+ if (is_null($blocksize) && !OS_WINDOWS) {
294+ return fwrite($this->fp, $data);
295+ } else {
296+ if (is_null($blocksize)) {
297+ $blocksize = 1024;
298+ }
299+
300+ $pos = 0;
301+ $size = strlen($data);
302+ while ($pos < $size) {
303+ $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
304+ if ($written === false) {
305+ return false;
306+ }
307+ $pos += $written;
308+ }
309+
310+ return $pos;
311+ }
312+ }
313+
314+ /**
315+ * Write a line of data to the socket, followed by a trailing "\r\n".
316+ *
317+ * @access public
318+ * @return mixed fputs result, or an error
319+ */
320+ function writeLine($data)
321+ {
322+ if (!is_resource($this->fp)) {
323+ return $this->raiseError('not connected');
324+ }
325+
326+ return fwrite($this->fp, $data . "\r\n");
327+ }
328+
329+ /**
330+ * Tests for end-of-file on a socket descriptor.
331+ *
332+ * @access public
333+ * @return bool
334+ */
335+ function eof()
336+ {
337+ return (is_resource($this->fp) && feof($this->fp));
338+ }
339+
340+ /**
341+ * Reads a byte of data
342+ *
343+ * @access public
344+ * @return 1 byte of data from the socket, or a PEAR_Error if
345+ * not connected.
346+ */
347+ function readByte()
348+ {
349+ if (!is_resource($this->fp)) {
350+ return $this->raiseError('not connected');
351+ }
352+
353+ return ord(@fread($this->fp, 1));
354+ }
355+
356+ /**
357+ * Reads a word of data
358+ *
359+ * @access public
360+ * @return 1 word of data from the socket, or a PEAR_Error if
361+ * not connected.
362+ */
363+ function readWord()
364+ {
365+ if (!is_resource($this->fp)) {
366+ return $this->raiseError('not connected');
367+ }
368+
369+ $buf = @fread($this->fp, 2);
370+ return (ord($buf[0]) + (ord($buf[1]) << 8));
371+ }
372+
373+ /**
374+ * Reads an int of data
375+ *
376+ * @access public
377+ * @return integer 1 int of data from the socket, or a PEAR_Error if
378+ * not connected.
379+ */
380+ function readInt()
381+ {
382+ if (!is_resource($this->fp)) {
383+ return $this->raiseError('not connected');
384+ }
385+
386+ $buf = @fread($this->fp, 4);
387+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
388+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
389+ }
390+
391+ /**
392+ * Reads a zero-terminated string of data
393+ *
394+ * @access public
395+ * @return string, or a PEAR_Error if
396+ * not connected.
397+ */
398+ function readString()
399+ {
400+ if (!is_resource($this->fp)) {
401+ return $this->raiseError('not connected');
402+ }
403+
404+ $string = '';
405+ while (($char = @fread($this->fp, 1)) != "\x00") {
406+ $string .= $char;
407+ }
408+ return $string;
409+ }
410+
411+ /**
412+ * Reads an IP Address and returns it in a dot formated string
413+ *
414+ * @access public
415+ * @return Dot formated string, or a PEAR_Error if
416+ * not connected.
417+ */
418+ function readIPAddress()
419+ {
420+ if (!is_resource($this->fp)) {
421+ return $this->raiseError('not connected');
422+ }
423+
424+ $buf = @fread($this->fp, 4);
425+ return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
426+ ord($buf[2]), ord($buf[3]));
427+ }
428+
429+ /**
430+ * Read until either the end of the socket or a newline, whichever
431+ * comes first. Strips the trailing newline from the returned data.
432+ *
433+ * @access public
434+ * @return All available data up to a newline, without that
435+ * newline, or until the end of the socket, or a PEAR_Error if
436+ * not connected.
437+ */
438+ function readLine()
439+ {
440+ if (!is_resource($this->fp)) {
441+ return $this->raiseError('not connected');
442+ }
443+
444+ $line = '';
445+ $timeout = time() + $this->timeout;
446+ while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
447+ $line .= @fgets($this->fp, $this->lineLength);
448+ if (substr($line, -1) == "\n") {
449+ return rtrim($line, "\r\n");
450+ }
451+ }
452+ return $line;
453+ }
454+
455+ /**
456+ * Read until the socket closes, or until there is no more data in
457+ * the inner PHP buffer. If the inner buffer is empty, in blocking
458+ * mode we wait for at least 1 byte of data. Therefore, in
459+ * blocking mode, if there is no data at all to be read, this
460+ * function will never exit (unless the socket is closed on the
461+ * remote end).
462+ *
463+ * @access public
464+ *
465+ * @return string All data until the socket closes, or a PEAR_Error if
466+ * not connected.
467+ */
468+ function readAll()
469+ {
470+ if (!is_resource($this->fp)) {
471+ return $this->raiseError('not connected');
472+ }
473+
474+ $data = '';
475+ while (!feof($this->fp)) {
476+ $data .= @fread($this->fp, $this->lineLength);
477+ }
478+ return $data;
479+ }
480+
481+ /**
482+ * Runs the equivalent of the select() system call on the socket
483+ * with a timeout specified by tv_sec and tv_usec.
484+ *
485+ * @param integer $state Which of read/write/error to check for.
486+ * @param integer $tv_sec Number of seconds for timeout.
487+ * @param integer $tv_usec Number of microseconds for timeout.
488+ *
489+ * @access public
490+ * @return False if select fails, integer describing which of read/write/error
491+ * are ready, or PEAR_Error if not connected.
492+ */
493+ function select($state, $tv_sec, $tv_usec = 0)
494+ {
495+ if (!is_resource($this->fp)) {
496+ return $this->raiseError('not connected');
497+ }
498+
499+ $read = null;
500+ $write = null;
501+ $except = null;
502+ if ($state & NET_SOCKET_READ) {
503+ $read[] = $this->fp;
504+ }
505+ if ($state & NET_SOCKET_WRITE) {
506+ $write[] = $this->fp;
507+ }
508+ if ($state & NET_SOCKET_ERROR) {
509+ $except[] = $this->fp;
510+ }
511+ if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
512+ return false;
513+ }
514+
515+ $result = 0;
516+ if (count($read)) {
517+ $result |= NET_SOCKET_READ;
518+ }
519+ if (count($write)) {
520+ $result |= NET_SOCKET_WRITE;
521+ }
522+ if (count($except)) {
523+ $result |= NET_SOCKET_ERROR;
524+ }
525+ return $result;
526+ }
527+
528+}
--- a/trunk/NP_Moblog/sharedlibs/Net/URL.php
+++ b/trunk/NP_Moblog/sharedlibs/Net/URL.php
@@ -1,410 +1,410 @@
1-<?php
2-// +-----------------------------------------------------------------------+
3-// | Copyright (c) 2002-2004, Richard Heyes |
4-// | All rights reserved. |
5-// | |
6-// | Redistribution and use in source and binary forms, with or without |
7-// | modification, are permitted provided that the following conditions |
8-// | are met: |
9-// | |
10-// | o Redistributions of source code must retain the above copyright |
11-// | notice, this list of conditions and the following disclaimer. |
12-// | o Redistributions in binary form must reproduce the above copyright |
13-// | notice, this list of conditions and the following disclaimer in the |
14-// | documentation and/or other materials provided with the distribution.|
15-// | o The names of the authors may not be used to endorse or promote |
16-// | products derived from this software without specific prior written |
17-// | permission. |
18-// | |
19-// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20-// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21-// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22-// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23-// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24-// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25-// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26-// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27-// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28-// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29-// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30-// | |
31-// +-----------------------------------------------------------------------+
32-// | Author: Richard Heyes <richard at php net> |
33-// +-----------------------------------------------------------------------+
34-//
35-// $Id: URL.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
36-//
37-// Net_URL Class
38-
39-class Net_URL
40-{
41- /**
42- * Full url
43- * @var string
44- */
45- var $url;
46-
47- /**
48- * Protocol
49- * @var string
50- */
51- var $protocol;
52-
53- /**
54- * Username
55- * @var string
56- */
57- var $username;
58-
59- /**
60- * Password
61- * @var string
62- */
63- var $password;
64-
65- /**
66- * Host
67- * @var string
68- */
69- var $host;
70-
71- /**
72- * Port
73- * @var integer
74- */
75- var $port;
76-
77- /**
78- * Path
79- * @var string
80- */
81- var $path;
82-
83- /**
84- * Query string
85- * @var array
86- */
87- var $querystring;
88-
89- /**
90- * Anchor
91- * @var string
92- */
93- var $anchor;
94-
95- /**
96- * Whether to use []
97- * @var bool
98- */
99- var $useBrackets;
100-
101- /**
102- * PHP4 Constructor
103- *
104- * @see __construct()
105- */
106- function Net_URL($url = null, $useBrackets = true)
107- {
108- $this->__construct($url, $useBrackets);
109- }
110-
111- /**
112- * PHP5 Constructor
113- *
114- * Parses the given url and stores the various parts
115- * Defaults are used in certain cases
116- *
117- * @param string $url Optional URL
118- * @param bool $useBrackets Whether to use square brackets when
119- * multiple querystrings with the same name
120- * exist
121- */
122- function __construct($url = null, $useBrackets = true)
123- {
124- $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
125-
126- $this->useBrackets = $useBrackets;
127- $this->url = $url;
128- $this->user = '';
129- $this->pass = '';
130- $this->host = '';
131- $this->port = 80;
132- $this->path = '';
133- $this->querystring = array();
134- $this->anchor = '';
135-
136- // Only use defaults if not an absolute URL given
137- if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
138-
139- $this->protocol = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');
140-
141- /**
142- * Figure out host/port
143- */
144- if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
145- $host = $matches[1];
146- if (!empty($matches[3])) {
147- $port = $matches[3];
148- } else {
149- $port = $this->getStandardPort($this->protocol);
150- }
151- }
152-
153- $this->user = '';
154- $this->pass = '';
155- $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
156- $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
157- $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
158- $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
159- $this->anchor = '';
160- }
161-
162- // Parse the url and store the various parts
163- if (!empty($url)) {
164- $urlinfo = parse_url($url);
165-
166- // Default querystring
167- $this->querystring = array();
168-
169- foreach ($urlinfo as $key => $value) {
170- switch ($key) {
171- case 'scheme':
172- $this->protocol = $value;
173- $this->port = $this->getStandardPort($value);
174- break;
175-
176- case 'user':
177- case 'pass':
178- case 'host':
179- case 'port':
180- $this->$key = $value;
181- break;
182-
183- case 'path':
184- if ($value{0} == '/') {
185- $this->path = $value;
186- } else {
187- $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
188- $this->path = sprintf('%s/%s', $path, $value);
189- }
190- break;
191-
192- case 'query':
193- $this->querystring = $this->_parseRawQueryString($value);
194- break;
195-
196- case 'fragment':
197- $this->anchor = $value;
198- break;
199- }
200- }
201- }
202- }
203-
204- /**
205- * Returns full url
206- *
207- * @return string Full url
208- * @access public
209- */
210- function getURL()
211- {
212- $querystring = $this->getQueryString();
213-
214- $this->url = $this->protocol . '://'
215- . $this->user . (!empty($this->pass) ? ':' : '')
216- . $this->pass . (!empty($this->user) ? '@' : '')
217- . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
218- . $this->path
219- . (!empty($querystring) ? '?' . $querystring : '')
220- . (!empty($this->anchor) ? '#' . $this->anchor : '');
221-
222- return $this->url;
223- }
224-
225- /**
226- * Adds a querystring item
227- *
228- * @param string $name Name of item
229- * @param string $value Value of item
230- * @param bool $preencoded Whether value is urlencoded or not, default = not
231- * @access public
232- */
233- function addQueryString($name, $value, $preencoded = false)
234- {
235- if ($preencoded) {
236- $this->querystring[$name] = $value;
237- } else {
238- $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
239- }
240- }
241-
242- /**
243- * Removes a querystring item
244- *
245- * @param string $name Name of item
246- * @access public
247- */
248- function removeQueryString($name)
249- {
250- if (isset($this->querystring[$name])) {
251- unset($this->querystring[$name]);
252- }
253- }
254-
255- /**
256- * Sets the querystring to literally what you supply
257- *
258- * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
259- * @access public
260- */
261- function addRawQueryString($querystring)
262- {
263- $this->querystring = $this->_parseRawQueryString($querystring);
264- }
265-
266- /**
267- * Returns flat querystring
268- *
269- * @return string Querystring
270- * @access public
271- */
272- function getQueryString()
273- {
274- if (!empty($this->querystring)) {
275- foreach ($this->querystring as $name => $value) {
276- if (is_array($value)) {
277- foreach ($value as $k => $v) {
278- $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
279- }
280- } elseif (!is_null($value)) {
281- $querystring[] = $name . '=' . $value;
282- } else {
283- $querystring[] = $name;
284- }
285- }
286- $querystring = implode(ini_get('arg_separator.output'), $querystring);
287- } else {
288- $querystring = '';
289- }
290-
291- return $querystring;
292- }
293-
294- /**
295- * Parses raw querystring and returns an array of it
296- *
297- * @param string $querystring The querystring to parse
298- * @return array An array of the querystring data
299- * @access private
300- */
301- function _parseRawQuerystring($querystring)
302- {
303- $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
304- $return = array();
305-
306- foreach ($parts as $part) {
307- if (strpos($part, '=') !== false) {
308- $value = substr($part, strpos($part, '=') + 1);
309- $key = substr($part, 0, strpos($part, '='));
310- } else {
311- $value = null;
312- $key = $part;
313- }
314- if (substr($key, -2) == '[]') {
315- $key = substr($key, 0, -2);
316- if (@!is_array($return[$key])) {
317- $return[$key] = array();
318- $return[$key][] = $value;
319- } else {
320- $return[$key][] = $value;
321- }
322- } elseif (!$this->useBrackets AND !empty($return[$key])) {
323- $return[$key] = (array)$return[$key];
324- $return[$key][] = $value;
325- } else {
326- $return[$key] = $value;
327- }
328- }
329-
330- return $return;
331- }
332-
333- /**
334- * Resolves //, ../ and ./ from a path and returns
335- * the result. Eg:
336- *
337- * /foo/bar/../boo.php => /foo/boo.php
338- * /foo/bar/../../boo.php => /boo.php
339- * /foo/bar/.././/boo.php => /foo/boo.php
340- *
341- * This method can also be called statically.
342- *
343- * @param string $url URL path to resolve
344- * @return string The result
345- */
346- function resolvePath($path)
347- {
348- $path = explode('/', str_replace('//', '/', $path));
349-
350- for ($i=0; $i<count($path); $i++) {
351- if ($path[$i] == '.') {
352- unset($path[$i]);
353- $path = array_values($path);
354- $i--;
355-
356- } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
357- unset($path[$i]);
358- unset($path[$i-1]);
359- $path = array_values($path);
360- $i -= 2;
361-
362- } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
363- unset($path[$i]);
364- $path = array_values($path);
365- $i--;
366-
367- } else {
368- continue;
369- }
370- }
371-
372- return implode('/', $path);
373- }
374-
375- /**
376- * Returns the standard port number for a protocol
377- *
378- * @param string $scheme The protocol to lookup
379- * @return integer Port number or NULL if no scheme matches
380- *
381- * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
382- */
383- function getStandardPort($scheme)
384- {
385- switch (strtolower($scheme)) {
386- case 'http': return 80;
387- case 'https': return 443;
388- case 'ftp': return 21;
389- case 'imap': return 143;
390- case 'imaps': return 993;
391- case 'pop3': return 110;
392- case 'pop3s': return 995;
393- default: return null;
394- }
395- }
396-
397- /**
398- * Forces the URL to a particular protocol
399- *
400- * @param string $protocol Protocol to force the URL to
401- * @param integer $port Optional port (standard port is used by default)
402- */
403- function setProtocol($protocol, $port = null)
404- {
405- $this->protocol = $protocol;
406- $this->port = is_null($port) ? $this->getStandardPort() : $port;
407- }
408-
409-}
410-?>
1+<?php
2+// +-----------------------------------------------------------------------+
3+// | Copyright (c) 2002-2004, Richard Heyes |
4+// | All rights reserved. |
5+// | |
6+// | Redistribution and use in source and binary forms, with or without |
7+// | modification, are permitted provided that the following conditions |
8+// | are met: |
9+// | |
10+// | o Redistributions of source code must retain the above copyright |
11+// | notice, this list of conditions and the following disclaimer. |
12+// | o Redistributions in binary form must reproduce the above copyright |
13+// | notice, this list of conditions and the following disclaimer in the |
14+// | documentation and/or other materials provided with the distribution.|
15+// | o The names of the authors may not be used to endorse or promote |
16+// | products derived from this software without specific prior written |
17+// | permission. |
18+// | |
19+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30+// | |
31+// +-----------------------------------------------------------------------+
32+// | Author: Richard Heyes <richard at php net> |
33+// +-----------------------------------------------------------------------+
34+//
35+// $Id: URL.php,v 1.1 2006/11/27 17:14:39 hsur Exp $
36+//
37+// Net_URL Class
38+
39+class Net_URL
40+{
41+ /**
42+ * Full url
43+ * @var string
44+ */
45+ var $url;
46+
47+ /**
48+ * Protocol
49+ * @var string
50+ */
51+ var $protocol;
52+
53+ /**
54+ * Username
55+ * @var string
56+ */
57+ var $username;
58+
59+ /**
60+ * Password
61+ * @var string
62+ */
63+ var $password;
64+
65+ /**
66+ * Host
67+ * @var string
68+ */
69+ var $host;
70+
71+ /**
72+ * Port
73+ * @var integer
74+ */
75+ var $port;
76+
77+ /**
78+ * Path
79+ * @var string
80+ */
81+ var $path;
82+
83+ /**
84+ * Query string
85+ * @var array
86+ */
87+ var $querystring;
88+
89+ /**
90+ * Anchor
91+ * @var string
92+ */
93+ var $anchor;
94+
95+ /**
96+ * Whether to use []
97+ * @var bool
98+ */
99+ var $useBrackets;
100+
101+ /**
102+ * PHP4 Constructor
103+ *
104+ * @see __construct()
105+ */
106+ function Net_URL($url = null, $useBrackets = true)
107+ {
108+ $this->__construct($url, $useBrackets);
109+ }
110+
111+ /**
112+ * PHP5 Constructor
113+ *
114+ * Parses the given url and stores the various parts
115+ * Defaults are used in certain cases
116+ *
117+ * @param string $url Optional URL
118+ * @param bool $useBrackets Whether to use square brackets when
119+ * multiple querystrings with the same name
120+ * exist
121+ */
122+ function __construct($url = null, $useBrackets = true)
123+ {
124+ $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
125+
126+ $this->useBrackets = $useBrackets;
127+ $this->url = $url;
128+ $this->user = '';
129+ $this->pass = '';
130+ $this->host = '';
131+ $this->port = 80;
132+ $this->path = '';
133+ $this->querystring = array();
134+ $this->anchor = '';
135+
136+ // Only use defaults if not an absolute URL given
137+ if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
138+
139+ $this->protocol = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');
140+
141+ /**
142+ * Figure out host/port
143+ */
144+ if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
145+ $host = $matches[1];
146+ if (!empty($matches[3])) {
147+ $port = $matches[3];
148+ } else {
149+ $port = $this->getStandardPort($this->protocol);
150+ }
151+ }
152+
153+ $this->user = '';
154+ $this->pass = '';
155+ $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
156+ $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
157+ $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
158+ $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
159+ $this->anchor = '';
160+ }
161+
162+ // Parse the url and store the various parts
163+ if (!empty($url)) {
164+ $urlinfo = parse_url($url);
165+
166+ // Default querystring
167+ $this->querystring = array();
168+
169+ foreach ($urlinfo as $key => $value) {
170+ switch ($key) {
171+ case 'scheme':
172+ $this->protocol = $value;
173+ $this->port = $this->getStandardPort($value);
174+ break;
175+
176+ case 'user':
177+ case 'pass':
178+ case 'host':
179+ case 'port':
180+ $this->$key = $value;
181+ break;
182+
183+ case 'path':
184+ if ($value{0} == '/') {
185+ $this->path = $value;
186+ } else {
187+ $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
188+ $this->path = sprintf('%s/%s', $path, $value);
189+ }
190+ break;
191+
192+ case 'query':
193+ $this->querystring = $this->_parseRawQueryString($value);
194+ break;
195+
196+ case 'fragment':
197+ $this->anchor = $value;
198+ break;
199+ }
200+ }
201+ }
202+ }
203+
204+ /**
205+ * Returns full url
206+ *
207+ * @return string Full url
208+ * @access public
209+ */
210+ function getURL()
211+ {
212+ $querystring = $this->getQueryString();
213+
214+ $this->url = $this->protocol . '://'
215+ . $this->user . (!empty($this->pass) ? ':' : '')
216+ . $this->pass . (!empty($this->user) ? '@' : '')
217+ . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
218+ . $this->path
219+ . (!empty($querystring) ? '?' . $querystring : '')
220+ . (!empty($this->anchor) ? '#' . $this->anchor : '');
221+
222+ return $this->url;
223+ }
224+
225+ /**
226+ * Adds a querystring item
227+ *
228+ * @param string $name Name of item
229+ * @param string $value Value of item
230+ * @param bool $preencoded Whether value is urlencoded or not, default = not
231+ * @access public
232+ */
233+ function addQueryString($name, $value, $preencoded = false)
234+ {
235+ if ($preencoded) {
236+ $this->querystring[$name] = $value;
237+ } else {
238+ $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
239+ }
240+ }
241+
242+ /**
243+ * Removes a querystring item
244+ *
245+ * @param string $name Name of item
246+ * @access public
247+ */
248+ function removeQueryString($name)
249+ {
250+ if (isset($this->querystring[$name])) {
251+ unset($this->querystring[$name]);
252+ }
253+ }
254+
255+ /**
256+ * Sets the querystring to literally what you supply
257+ *
258+ * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
259+ * @access public
260+ */
261+ function addRawQueryString($querystring)
262+ {
263+ $this->querystring = $this->_parseRawQueryString($querystring);
264+ }
265+
266+ /**
267+ * Returns flat querystring
268+ *
269+ * @return string Querystring
270+ * @access public
271+ */
272+ function getQueryString()
273+ {
274+ if (!empty($this->querystring)) {
275+ foreach ($this->querystring as $name => $value) {
276+ if (is_array($value)) {
277+ foreach ($value as $k => $v) {
278+ $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
279+ }
280+ } elseif (!is_null($value)) {
281+ $querystring[] = $name . '=' . $value;
282+ } else {
283+ $querystring[] = $name;
284+ }
285+ }
286+ $querystring = implode(ini_get('arg_separator.output'), $querystring);
287+ } else {
288+ $querystring = '';
289+ }
290+
291+ return $querystring;
292+ }
293+
294+ /**
295+ * Parses raw querystring and returns an array of it
296+ *
297+ * @param string $querystring The querystring to parse
298+ * @return array An array of the querystring data
299+ * @access private
300+ */
301+ function _parseRawQuerystring($querystring)
302+ {
303+ $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
304+ $return = array();
305+
306+ foreach ($parts as $part) {
307+ if (strpos($part, '=') !== false) {
308+ $value = substr($part, strpos($part, '=') + 1);
309+ $key = substr($part, 0, strpos($part, '='));
310+ } else {
311+ $value = null;
312+ $key = $part;
313+ }
314+ if (substr($key, -2) == '[]') {
315+ $key = substr($key, 0, -2);
316+ if (@!is_array($return[$key])) {
317+ $return[$key] = array();
318+ $return[$key][] = $value;
319+ } else {
320+ $return[$key][] = $value;
321+ }
322+ } elseif (!$this->useBrackets AND !empty($return[$key])) {
323+ $return[$key] = (array)$return[$key];
324+ $return[$key][] = $value;
325+ } else {
326+ $return[$key] = $value;
327+ }
328+ }
329+
330+ return $return;
331+ }
332+
333+ /**
334+ * Resolves //, ../ and ./ from a path and returns
335+ * the result. Eg:
336+ *
337+ * /foo/bar/../boo.php => /foo/boo.php
338+ * /foo/bar/../../boo.php => /boo.php
339+ * /foo/bar/.././/boo.php => /foo/boo.php
340+ *
341+ * This method can also be called statically.
342+ *
343+ * @param string $url URL path to resolve
344+ * @return string The result
345+ */
346+ function resolvePath($path)
347+ {
348+ $path = explode('/', str_replace('//', '/', $path));
349+
350+ for ($i=0; $i<count($path); $i++) {
351+ if ($path[$i] == '.') {
352+ unset($path[$i]);
353+ $path = array_values($path);
354+ $i--;
355+
356+ } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
357+ unset($path[$i]);
358+ unset($path[$i-1]);
359+ $path = array_values($path);
360+ $i -= 2;
361+
362+ } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
363+ unset($path[$i]);
364+ $path = array_values($path);
365+ $i--;
366+
367+ } else {
368+ continue;
369+ }
370+ }
371+
372+ return implode('/', $path);
373+ }
374+
375+ /**
376+ * Returns the standard port number for a protocol
377+ *
378+ * @param string $scheme The protocol to lookup
379+ * @return integer Port number or NULL if no scheme matches
380+ *
381+ * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
382+ */
383+ function getStandardPort($scheme)
384+ {
385+ switch (strtolower($scheme)) {
386+ case 'http': return 80;
387+ case 'https': return 443;
388+ case 'ftp': return 21;
389+ case 'imap': return 143;
390+ case 'imaps': return 993;
391+ case 'pop3': return 110;
392+ case 'pop3s': return 995;
393+ default: return null;
394+ }
395+ }
396+
397+ /**
398+ * Forces the URL to a particular protocol
399+ *
400+ * @param string $protocol Protocol to force the URL to
401+ * @param integer $port Optional port (standard port is used by default)
402+ */
403+ function setProtocol($protocol, $port = null)
404+ {
405+ $this->protocol = $protocol;
406+ $this->port = is_null($port) ? $this->getStandardPort() : $port;
407+ }
408+
409+}
410+?>
--- a/trunk/NP_Moblog/sharedlibs/PEAR.php
+++ b/trunk/NP_Moblog/sharedlibs/PEAR.php
@@ -1,1101 +1,1101 @@
1-<?php
2-/**
3- * PEAR, the PHP Extension and Application Repository
4- *
5- * PEAR class and PEAR_Error class
6- *
7- * PHP versions 4 and 5
8- *
9- * LICENSE: This source file is subject to version 3.0 of the PHP license
10- * that is available through the world-wide-web at the following URI:
11- * http://www.php.net/license/3_0.txt. If you did not receive a copy of
12- * the PHP License and are unable to obtain it through the web, please
13- * send a note to license@php.net so we can mail you a copy immediately.
14- *
15- * @category pear
16- * @package PEAR
17- * @author Sterling Hughes <sterling@php.net>
18- * @author Stig Bakken <ssb@php.net>
19- * @author Tomas V.V.Cox <cox@idecnet.com>
20- * @author Greg Beaver <cellog@php.net>
21- * @copyright 1997-2006 The PHP Group
22- * @license http://www.php.net/license/3_0.txt PHP License 3.0
23- * @version CVS: $Id: PEAR.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
24- * @link http://pear.php.net/package/PEAR
25- * @since File available since Release 0.1
26- */
27-
28-/**#@+
29- * ERROR constants
30- */
31-define('PEAR_ERROR_RETURN', 1);
32-define('PEAR_ERROR_PRINT', 2);
33-define('PEAR_ERROR_TRIGGER', 4);
34-define('PEAR_ERROR_DIE', 8);
35-define('PEAR_ERROR_CALLBACK', 16);
36-/**
37- * WARNING: obsolete
38- * @deprecated
39- */
40-define('PEAR_ERROR_EXCEPTION', 32);
41-/**#@-*/
42-define('PEAR_ZE2', (function_exists('version_compare') &&
43- version_compare(zend_version(), "2-dev", "ge")));
44-
45-if (substr(PHP_OS, 0, 3) == 'WIN') {
46- define('OS_WINDOWS', true);
47- define('OS_UNIX', false);
48- define('PEAR_OS', 'Windows');
49-} else {
50- define('OS_WINDOWS', false);
51- define('OS_UNIX', true);
52- define('PEAR_OS', 'Unix'); // blatant assumption
53-}
54-
55-// instant backwards compatibility
56-if (!defined('PATH_SEPARATOR')) {
57- if (OS_WINDOWS) {
58- define('PATH_SEPARATOR', ';');
59- } else {
60- define('PATH_SEPARATOR', ':');
61- }
62-}
63-
64-$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
65-$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
66-$GLOBALS['_PEAR_destructor_object_list'] = array();
67-$GLOBALS['_PEAR_shutdown_funcs'] = array();
68-$GLOBALS['_PEAR_error_handler_stack'] = array();
69-
70-@ini_set('track_errors', true);
71-
72-/**
73- * Base class for other PEAR classes. Provides rudimentary
74- * emulation of destructors.
75- *
76- * If you want a destructor in your class, inherit PEAR and make a
77- * destructor method called _yourclassname (same name as the
78- * constructor, but with a "_" prefix). Also, in your constructor you
79- * have to call the PEAR constructor: $this->PEAR();.
80- * The destructor method will be called without parameters. Note that
81- * at in some SAPI implementations (such as Apache), any output during
82- * the request shutdown (in which destructors are called) seems to be
83- * discarded. If you need to get any debug information from your
84- * destructor, use error_log(), syslog() or something similar.
85- *
86- * IMPORTANT! To use the emulated destructors you need to create the
87- * objects by reference: $obj =& new PEAR_child;
88- *
89- * @category pear
90- * @package PEAR
91- * @author Stig Bakken <ssb@php.net>
92- * @author Tomas V.V. Cox <cox@idecnet.com>
93- * @author Greg Beaver <cellog@php.net>
94- * @copyright 1997-2006 The PHP Group
95- * @license http://www.php.net/license/3_0.txt PHP License 3.0
96- * @version Release: 1.4.9
97- * @link http://pear.php.net/package/PEAR
98- * @see PEAR_Error
99- * @since Class available since PHP 4.0.2
100- * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
101- */
102-class PEAR
103-{
104- // {{{ properties
105-
106- /**
107- * Whether to enable internal debug messages.
108- *
109- * @var bool
110- * @access private
111- */
112- var $_debug = false;
113-
114- /**
115- * Default error mode for this object.
116- *
117- * @var int
118- * @access private
119- */
120- var $_default_error_mode = null;
121-
122- /**
123- * Default error options used for this object when error mode
124- * is PEAR_ERROR_TRIGGER.
125- *
126- * @var int
127- * @access private
128- */
129- var $_default_error_options = null;
130-
131- /**
132- * Default error handler (callback) for this object, if error mode is
133- * PEAR_ERROR_CALLBACK.
134- *
135- * @var string
136- * @access private
137- */
138- var $_default_error_handler = '';
139-
140- /**
141- * Which class to use for error objects.
142- *
143- * @var string
144- * @access private
145- */
146- var $_error_class = 'PEAR_Error';
147-
148- /**
149- * An array of expected errors.
150- *
151- * @var array
152- * @access private
153- */
154- var $_expected_errors = array();
155-
156- // }}}
157-
158- // {{{ constructor
159-
160- /**
161- * Constructor. Registers this object in
162- * $_PEAR_destructor_object_list for destructor emulation if a
163- * destructor object exists.
164- *
165- * @param string $error_class (optional) which class to use for
166- * error objects, defaults to PEAR_Error.
167- * @access public
168- * @return void
169- */
170- function PEAR($error_class = null)
171- {
172- $classname = strtolower(get_class($this));
173- if ($this->_debug) {
174- print "PEAR constructor called, class=$classname\n";
175- }
176- if ($error_class !== null) {
177- $this->_error_class = $error_class;
178- }
179- while ($classname && strcasecmp($classname, "pear")) {
180- $destructor = "_$classname";
181- if (method_exists($this, $destructor)) {
182- global $_PEAR_destructor_object_list;
183- $_PEAR_destructor_object_list[] = &$this;
184- if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
185- register_shutdown_function("_PEAR_call_destructors");
186- $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
187- }
188- break;
189- } else {
190- $classname = get_parent_class($classname);
191- }
192- }
193- }
194-
195- // }}}
196- // {{{ destructor
197-
198- /**
199- * Destructor (the emulated type of...). Does nothing right now,
200- * but is included for forward compatibility, so subclass
201- * destructors should always call it.
202- *
203- * See the note in the class desciption about output from
204- * destructors.
205- *
206- * @access public
207- * @return void
208- */
209- function _PEAR() {
210- if ($this->_debug) {
211- printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
212- }
213- }
214-
215- // }}}
216- // {{{ getStaticProperty()
217-
218- /**
219- * If you have a class that's mostly/entirely static, and you need static
220- * properties, you can use this method to simulate them. Eg. in your method(s)
221- * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
222- * You MUST use a reference, or they will not persist!
223- *
224- * @access public
225- * @param string $class The calling classname, to prevent clashes
226- * @param string $var The variable to retrieve.
227- * @return mixed A reference to the variable. If not set it will be
228- * auto initialised to NULL.
229- */
230- function &getStaticProperty($class, $var)
231- {
232- static $properties;
233- return $properties[$class][$var];
234- }
235-
236- // }}}
237- // {{{ registerShutdownFunc()
238-
239- /**
240- * Use this function to register a shutdown method for static
241- * classes.
242- *
243- * @access public
244- * @param mixed $func The function name (or array of class/method) to call
245- * @param mixed $args The arguments to pass to the function
246- * @return void
247- */
248- function registerShutdownFunc($func, $args = array())
249- {
250- // if we are called statically, there is a potential
251- // that no shutdown func is registered. Bug #6445
252- if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
253- register_shutdown_function("_PEAR_call_destructors");
254- $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
255- }
256- $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
257- }
258-
259- // }}}
260- // {{{ isError()
261-
262- /**
263- * Tell whether a value is a PEAR error.
264- *
265- * @param mixed $data the value to test
266- * @param int $code if $data is an error object, return true
267- * only if $code is a string and
268- * $obj->getMessage() == $code or
269- * $code is an integer and $obj->getCode() == $code
270- * @access public
271- * @return bool true if parameter is an error
272- */
273- function isError($data, $code = null)
274- {
275- if (is_a($data, 'PEAR_Error')) {
276- if (is_null($code)) {
277- return true;
278- } elseif (is_string($code)) {
279- return $data->getMessage() == $code;
280- } else {
281- return $data->getCode() == $code;
282- }
283- }
284- return false;
285- }
286-
287- // }}}
288- // {{{ setErrorHandling()
289-
290- /**
291- * Sets how errors generated by this object should be handled.
292- * Can be invoked both in objects and statically. If called
293- * statically, setErrorHandling sets the default behaviour for all
294- * PEAR objects. If called in an object, setErrorHandling sets
295- * the default behaviour for that object.
296- *
297- * @param int $mode
298- * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
299- * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
300- * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
301- *
302- * @param mixed $options
303- * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
304- * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
305- *
306- * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
307- * to be the callback function or method. A callback
308- * function is a string with the name of the function, a
309- * callback method is an array of two elements: the element
310- * at index 0 is the object, and the element at index 1 is
311- * the name of the method to call in the object.
312- *
313- * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
314- * a printf format string used when printing the error
315- * message.
316- *
317- * @access public
318- * @return void
319- * @see PEAR_ERROR_RETURN
320- * @see PEAR_ERROR_PRINT
321- * @see PEAR_ERROR_TRIGGER
322- * @see PEAR_ERROR_DIE
323- * @see PEAR_ERROR_CALLBACK
324- * @see PEAR_ERROR_EXCEPTION
325- *
326- * @since PHP 4.0.5
327- */
328-
329- function setErrorHandling($mode = null, $options = null)
330- {
331- if (isset($this) && is_a($this, 'PEAR')) {
332- $setmode = &$this->_default_error_mode;
333- $setoptions = &$this->_default_error_options;
334- } else {
335- $setmode = &$GLOBALS['_PEAR_default_error_mode'];
336- $setoptions = &$GLOBALS['_PEAR_default_error_options'];
337- }
338-
339- switch ($mode) {
340- case PEAR_ERROR_EXCEPTION:
341- case PEAR_ERROR_RETURN:
342- case PEAR_ERROR_PRINT:
343- case PEAR_ERROR_TRIGGER:
344- case PEAR_ERROR_DIE:
345- case null:
346- $setmode = $mode;
347- $setoptions = $options;
348- break;
349-
350- case PEAR_ERROR_CALLBACK:
351- $setmode = $mode;
352- // class/object method callback
353- if (is_callable($options)) {
354- $setoptions = $options;
355- } else {
356- trigger_error("invalid error callback", E_USER_WARNING);
357- }
358- break;
359-
360- default:
361- trigger_error("invalid error mode", E_USER_WARNING);
362- break;
363- }
364- }
365-
366- // }}}
367- // {{{ expectError()
368-
369- /**
370- * This method is used to tell which errors you expect to get.
371- * Expected errors are always returned with error mode
372- * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
373- * and this method pushes a new element onto it. The list of
374- * expected errors are in effect until they are popped off the
375- * stack with the popExpect() method.
376- *
377- * Note that this method can not be called statically
378- *
379- * @param mixed $code a single error code or an array of error codes to expect
380- *
381- * @return int the new depth of the "expected errors" stack
382- * @access public
383- */
384- function expectError($code = '*')
385- {
386- if (is_array($code)) {
387- array_push($this->_expected_errors, $code);
388- } else {
389- array_push($this->_expected_errors, array($code));
390- }
391- return sizeof($this->_expected_errors);
392- }
393-
394- // }}}
395- // {{{ popExpect()
396-
397- /**
398- * This method pops one element off the expected error codes
399- * stack.
400- *
401- * @return array the list of error codes that were popped
402- */
403- function popExpect()
404- {
405- return array_pop($this->_expected_errors);
406- }
407-
408- // }}}
409- // {{{ _checkDelExpect()
410-
411- /**
412- * This method checks unsets an error code if available
413- *
414- * @param mixed error code
415- * @return bool true if the error code was unset, false otherwise
416- * @access private
417- * @since PHP 4.3.0
418- */
419- function _checkDelExpect($error_code)
420- {
421- $deleted = false;
422-
423- foreach ($this->_expected_errors AS $key => $error_array) {
424- if (in_array($error_code, $error_array)) {
425- unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
426- $deleted = true;
427- }
428-
429- // clean up empty arrays
430- if (0 == count($this->_expected_errors[$key])) {
431- unset($this->_expected_errors[$key]);
432- }
433- }
434- return $deleted;
435- }
436-
437- // }}}
438- // {{{ delExpect()
439-
440- /**
441- * This method deletes all occurences of the specified element from
442- * the expected error codes stack.
443- *
444- * @param mixed $error_code error code that should be deleted
445- * @return mixed list of error codes that were deleted or error
446- * @access public
447- * @since PHP 4.3.0
448- */
449- function delExpect($error_code)
450- {
451- $deleted = false;
452-
453- if ((is_array($error_code) && (0 != count($error_code)))) {
454- // $error_code is a non-empty array here;
455- // we walk through it trying to unset all
456- // values
457- foreach($error_code as $key => $error) {
458- if ($this->_checkDelExpect($error)) {
459- $deleted = true;
460- } else {
461- $deleted = false;
462- }
463- }
464- return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
465- } elseif (!empty($error_code)) {
466- // $error_code comes alone, trying to unset it
467- if ($this->_checkDelExpect($error_code)) {
468- return true;
469- } else {
470- return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
471- }
472- } else {
473- // $error_code is empty
474- return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
475- }
476- }
477-
478- // }}}
479- // {{{ raiseError()
480-
481- /**
482- * This method is a wrapper that returns an instance of the
483- * configured error class with this object's default error
484- * handling applied. If the $mode and $options parameters are not
485- * specified, the object's defaults are used.
486- *
487- * @param mixed $message a text error message or a PEAR error object
488- *
489- * @param int $code a numeric error code (it is up to your class
490- * to define these if you want to use codes)
491- *
492- * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
493- * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
494- * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
495- *
496- * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
497- * specifies the PHP-internal error level (one of
498- * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
499- * If $mode is PEAR_ERROR_CALLBACK, this
500- * parameter specifies the callback function or
501- * method. In other error modes this parameter
502- * is ignored.
503- *
504- * @param string $userinfo If you need to pass along for example debug
505- * information, this parameter is meant for that.
506- *
507- * @param string $error_class The returned error object will be
508- * instantiated from this class, if specified.
509- *
510- * @param bool $skipmsg If true, raiseError will only pass error codes,
511- * the error message parameter will be dropped.
512- *
513- * @access public
514- * @return object a PEAR error object
515- * @see PEAR::setErrorHandling
516- * @since PHP 4.0.5
517- */
518- function &raiseError($message = null,
519- $code = null,
520- $mode = null,
521- $options = null,
522- $userinfo = null,
523- $error_class = null,
524- $skipmsg = false)
525- {
526- // The error is yet a PEAR error object
527- if (is_object($message)) {
528- $code = $message->getCode();
529- $userinfo = $message->getUserInfo();
530- $error_class = $message->getType();
531- $message->error_message_prefix = '';
532- $message = $message->getMessage();
533- }
534-
535- if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
536- if ($exp[0] == "*" ||
537- (is_int(reset($exp)) && in_array($code, $exp)) ||
538- (is_string(reset($exp)) && in_array($message, $exp))) {
539- $mode = PEAR_ERROR_RETURN;
540- }
541- }
542- // No mode given, try global ones
543- if ($mode === null) {
544- // Class error handler
545- if (isset($this) && isset($this->_default_error_mode)) {
546- $mode = $this->_default_error_mode;
547- $options = $this->_default_error_options;
548- // Global error handler
549- } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
550- $mode = $GLOBALS['_PEAR_default_error_mode'];
551- $options = $GLOBALS['_PEAR_default_error_options'];
552- }
553- }
554-
555- if ($error_class !== null) {
556- $ec = $error_class;
557- } elseif (isset($this) && isset($this->_error_class)) {
558- $ec = $this->_error_class;
559- } else {
560- $ec = 'PEAR_Error';
561- }
562- if ($skipmsg) {
563- $a = &new $ec($code, $mode, $options, $userinfo);
564- return $a;
565- } else {
566- $a = &new $ec($message, $code, $mode, $options, $userinfo);
567- return $a;
568- }
569- }
570-
571- // }}}
572- // {{{ throwError()
573-
574- /**
575- * Simpler form of raiseError with fewer options. In most cases
576- * message, code and userinfo are enough.
577- *
578- * @param string $message
579- *
580- */
581- function &throwError($message = null,
582- $code = null,
583- $userinfo = null)
584- {
585- if (isset($this) && is_a($this, 'PEAR')) {
586- $a = &$this->raiseError($message, $code, null, null, $userinfo);
587- return $a;
588- } else {
589- $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
590- return $a;
591- }
592- }
593-
594- // }}}
595- function staticPushErrorHandling($mode, $options = null)
596- {
597- $stack = &$GLOBALS['_PEAR_error_handler_stack'];
598- $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
599- $def_options = &$GLOBALS['_PEAR_default_error_options'];
600- $stack[] = array($def_mode, $def_options);
601- switch ($mode) {
602- case PEAR_ERROR_EXCEPTION:
603- case PEAR_ERROR_RETURN:
604- case PEAR_ERROR_PRINT:
605- case PEAR_ERROR_TRIGGER:
606- case PEAR_ERROR_DIE:
607- case null:
608- $def_mode = $mode;
609- $def_options = $options;
610- break;
611-
612- case PEAR_ERROR_CALLBACK:
613- $def_mode = $mode;
614- // class/object method callback
615- if (is_callable($options)) {
616- $def_options = $options;
617- } else {
618- trigger_error("invalid error callback", E_USER_WARNING);
619- }
620- break;
621-
622- default:
623- trigger_error("invalid error mode", E_USER_WARNING);
624- break;
625- }
626- $stack[] = array($mode, $options);
627- return true;
628- }
629-
630- function staticPopErrorHandling()
631- {
632- $stack = &$GLOBALS['_PEAR_error_handler_stack'];
633- $setmode = &$GLOBALS['_PEAR_default_error_mode'];
634- $setoptions = &$GLOBALS['_PEAR_default_error_options'];
635- array_pop($stack);
636- list($mode, $options) = $stack[sizeof($stack) - 1];
637- array_pop($stack);
638- switch ($mode) {
639- case PEAR_ERROR_EXCEPTION:
640- case PEAR_ERROR_RETURN:
641- case PEAR_ERROR_PRINT:
642- case PEAR_ERROR_TRIGGER:
643- case PEAR_ERROR_DIE:
644- case null:
645- $setmode = $mode;
646- $setoptions = $options;
647- break;
648-
649- case PEAR_ERROR_CALLBACK:
650- $setmode = $mode;
651- // class/object method callback
652- if (is_callable($options)) {
653- $setoptions = $options;
654- } else {
655- trigger_error("invalid error callback", E_USER_WARNING);
656- }
657- break;
658-
659- default:
660- trigger_error("invalid error mode", E_USER_WARNING);
661- break;
662- }
663- return true;
664- }
665-
666- // {{{ pushErrorHandling()
667-
668- /**
669- * Push a new error handler on top of the error handler options stack. With this
670- * you can easily override the actual error handler for some code and restore
671- * it later with popErrorHandling.
672- *
673- * @param mixed $mode (same as setErrorHandling)
674- * @param mixed $options (same as setErrorHandling)
675- *
676- * @return bool Always true
677- *
678- * @see PEAR::setErrorHandling
679- */
680- function pushErrorHandling($mode, $options = null)
681- {
682- $stack = &$GLOBALS['_PEAR_error_handler_stack'];
683- if (isset($this) && is_a($this, 'PEAR')) {
684- $def_mode = &$this->_default_error_mode;
685- $def_options = &$this->_default_error_options;
686- } else {
687- $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
688- $def_options = &$GLOBALS['_PEAR_default_error_options'];
689- }
690- $stack[] = array($def_mode, $def_options);
691-
692- if (isset($this) && is_a($this, 'PEAR')) {
693- $this->setErrorHandling($mode, $options);
694- } else {
695- PEAR::setErrorHandling($mode, $options);
696- }
697- $stack[] = array($mode, $options);
698- return true;
699- }
700-
701- // }}}
702- // {{{ popErrorHandling()
703-
704- /**
705- * Pop the last error handler used
706- *
707- * @return bool Always true
708- *
709- * @see PEAR::pushErrorHandling
710- */
711- function popErrorHandling()
712- {
713- $stack = &$GLOBALS['_PEAR_error_handler_stack'];
714- array_pop($stack);
715- list($mode, $options) = $stack[sizeof($stack) - 1];
716- array_pop($stack);
717- if (isset($this) && is_a($this, 'PEAR')) {
718- $this->setErrorHandling($mode, $options);
719- } else {
720- PEAR::setErrorHandling($mode, $options);
721- }
722- return true;
723- }
724-
725- // }}}
726- // {{{ loadExtension()
727-
728- /**
729- * OS independant PHP extension load. Remember to take care
730- * on the correct extension name for case sensitive OSes.
731- *
732- * @param string $ext The extension name
733- * @return bool Success or not on the dl() call
734- */
735- function loadExtension($ext)
736- {
737- if (!extension_loaded($ext)) {
738- // if either returns true dl() will produce a FATAL error, stop that
739- if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
740- return false;
741- }
742- if (OS_WINDOWS) {
743- $suffix = '.dll';
744- } elseif (PHP_OS == 'HP-UX') {
745- $suffix = '.sl';
746- } elseif (PHP_OS == 'AIX') {
747- $suffix = '.a';
748- } elseif (PHP_OS == 'OSX') {
749- $suffix = '.bundle';
750- } else {
751- $suffix = '.so';
752- }
753- return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
754- }
755- return true;
756- }
757-
758- // }}}
759-}
760-
761-// {{{ _PEAR_call_destructors()
762-
763-function _PEAR_call_destructors()
764-{
765- global $_PEAR_destructor_object_list;
766- if (is_array($_PEAR_destructor_object_list) &&
767- sizeof($_PEAR_destructor_object_list))
768- {
769- reset($_PEAR_destructor_object_list);
770- if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
771- $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
772- }
773- while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
774- $classname = get_class($objref);
775- while ($classname) {
776- $destructor = "_$classname";
777- if (method_exists($objref, $destructor)) {
778- $objref->$destructor();
779- break;
780- } else {
781- $classname = get_parent_class($classname);
782- }
783- }
784- }
785- // Empty the object list to ensure that destructors are
786- // not called more than once.
787- $_PEAR_destructor_object_list = array();
788- }
789-
790- // Now call the shutdown functions
791- if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
792- foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
793- call_user_func_array($value[0], $value[1]);
794- }
795- }
796-}
797-
798-// }}}
799-/**
800- * Standard PEAR error class for PHP 4
801- *
802- * This class is supserseded by {@link PEAR_Exception} in PHP 5
803- *
804- * @category pear
805- * @package PEAR
806- * @author Stig Bakken <ssb@php.net>
807- * @author Tomas V.V. Cox <cox@idecnet.com>
808- * @author Gregory Beaver <cellog@php.net>
809- * @copyright 1997-2006 The PHP Group
810- * @license http://www.php.net/license/3_0.txt PHP License 3.0
811- * @version Release: 1.4.9
812- * @link http://pear.php.net/manual/en/core.pear.pear-error.php
813- * @see PEAR::raiseError(), PEAR::throwError()
814- * @since Class available since PHP 4.0.2
815- */
816-class PEAR_Error
817-{
818- // {{{ properties
819-
820- var $error_message_prefix = '';
821- var $mode = PEAR_ERROR_RETURN;
822- var $level = E_USER_NOTICE;
823- var $code = -1;
824- var $message = '';
825- var $userinfo = '';
826- var $backtrace = null;
827-
828- // }}}
829- // {{{ constructor
830-
831- /**
832- * PEAR_Error constructor
833- *
834- * @param string $message message
835- *
836- * @param int $code (optional) error code
837- *
838- * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
839- * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
840- * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
841- *
842- * @param mixed $options (optional) error level, _OR_ in the case of
843- * PEAR_ERROR_CALLBACK, the callback function or object/method
844- * tuple.
845- *
846- * @param string $userinfo (optional) additional user/debug info
847- *
848- * @access public
849- *
850- */
851- function PEAR_Error($message = 'unknown error', $code = null,
852- $mode = null, $options = null, $userinfo = null)
853- {
854- if ($mode === null) {
855- $mode = PEAR_ERROR_RETURN;
856- }
857- $this->message = $message;
858- $this->code = $code;
859- $this->mode = $mode;
860- $this->userinfo = $userinfo;
861- if (function_exists("debug_backtrace")) {
862- if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
863- $this->backtrace = debug_backtrace();
864- }
865- }
866- if ($mode & PEAR_ERROR_CALLBACK) {
867- $this->level = E_USER_NOTICE;
868- $this->callback = $options;
869- } else {
870- if ($options === null) {
871- $options = E_USER_NOTICE;
872- }
873- $this->level = $options;
874- $this->callback = null;
875- }
876- if ($this->mode & PEAR_ERROR_PRINT) {
877- if (is_null($options) || is_int($options)) {
878- $format = "%s";
879- } else {
880- $format = $options;
881- }
882- printf($format, $this->getMessage());
883- }
884- if ($this->mode & PEAR_ERROR_TRIGGER) {
885- trigger_error($this->getMessage(), $this->level);
886- }
887- if ($this->mode & PEAR_ERROR_DIE) {
888- $msg = $this->getMessage();
889- if (is_null($options) || is_int($options)) {
890- $format = "%s";
891- if (substr($msg, -1) != "\n") {
892- $msg .= "\n";
893- }
894- } else {
895- $format = $options;
896- }
897- die(sprintf($format, $msg));
898- }
899- if ($this->mode & PEAR_ERROR_CALLBACK) {
900- if (is_callable($this->callback)) {
901- call_user_func($this->callback, $this);
902- }
903- }
904- if ($this->mode & PEAR_ERROR_EXCEPTION) {
905- trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
906- eval('$e = new Exception($this->message, $this->code);throw($e);');
907- }
908- }
909-
910- // }}}
911- // {{{ getMode()
912-
913- /**
914- * Get the error mode from an error object.
915- *
916- * @return int error mode
917- * @access public
918- */
919- function getMode() {
920- return $this->mode;
921- }
922-
923- // }}}
924- // {{{ getCallback()
925-
926- /**
927- * Get the callback function/method from an error object.
928- *
929- * @return mixed callback function or object/method array
930- * @access public
931- */
932- function getCallback() {
933- return $this->callback;
934- }
935-
936- // }}}
937- // {{{ getMessage()
938-
939-
940- /**
941- * Get the error message from an error object.
942- *
943- * @return string full error message
944- * @access public
945- */
946- function getMessage()
947- {
948- return ($this->error_message_prefix . $this->message);
949- }
950-
951-
952- // }}}
953- // {{{ getCode()
954-
955- /**
956- * Get error code from an error object
957- *
958- * @return int error code
959- * @access public
960- */
961- function getCode()
962- {
963- return $this->code;
964- }
965-
966- // }}}
967- // {{{ getType()
968-
969- /**
970- * Get the name of this error/exception.
971- *
972- * @return string error/exception name (type)
973- * @access public
974- */
975- function getType()
976- {
977- return get_class($this);
978- }
979-
980- // }}}
981- // {{{ getUserInfo()
982-
983- /**
984- * Get additional user-supplied information.
985- *
986- * @return string user-supplied information
987- * @access public
988- */
989- function getUserInfo()
990- {
991- return $this->userinfo;
992- }
993-
994- // }}}
995- // {{{ getDebugInfo()
996-
997- /**
998- * Get additional debug information supplied by the application.
999- *
1000- * @return string debug information
1001- * @access public
1002- */
1003- function getDebugInfo()
1004- {
1005- return $this->getUserInfo();
1006- }
1007-
1008- // }}}
1009- // {{{ getBacktrace()
1010-
1011- /**
1012- * Get the call backtrace from where the error was generated.
1013- * Supported with PHP 4.3.0 or newer.
1014- *
1015- * @param int $frame (optional) what frame to fetch
1016- * @return array Backtrace, or NULL if not available.
1017- * @access public
1018- */
1019- function getBacktrace($frame = null)
1020- {
1021- if (defined('PEAR_IGNORE_BACKTRACE')) {
1022- return null;
1023- }
1024- if ($frame === null) {
1025- return $this->backtrace;
1026- }
1027- return $this->backtrace[$frame];
1028- }
1029-
1030- // }}}
1031- // {{{ addUserInfo()
1032-
1033- function addUserInfo($info)
1034- {
1035- if (empty($this->userinfo)) {
1036- $this->userinfo = $info;
1037- } else {
1038- $this->userinfo .= " ** $info";
1039- }
1040- }
1041-
1042- // }}}
1043- // {{{ toString()
1044-
1045- /**
1046- * Make a string representation of this object.
1047- *
1048- * @return string a string with an object summary
1049- * @access public
1050- */
1051- function toString() {
1052- $modes = array();
1053- $levels = array(E_USER_NOTICE => 'notice',
1054- E_USER_WARNING => 'warning',
1055- E_USER_ERROR => 'error');
1056- if ($this->mode & PEAR_ERROR_CALLBACK) {
1057- if (is_array($this->callback)) {
1058- $callback = (is_object($this->callback[0]) ?
1059- strtolower(get_class($this->callback[0])) :
1060- $this->callback[0]) . '::' .
1061- $this->callback[1];
1062- } else {
1063- $callback = $this->callback;
1064- }
1065- return sprintf('[%s: message="%s" code=%d mode=callback '.
1066- 'callback=%s prefix="%s" info="%s"]',
1067- strtolower(get_class($this)), $this->message, $this->code,
1068- $callback, $this->error_message_prefix,
1069- $this->userinfo);
1070- }
1071- if ($this->mode & PEAR_ERROR_PRINT) {
1072- $modes[] = 'print';
1073- }
1074- if ($this->mode & PEAR_ERROR_TRIGGER) {
1075- $modes[] = 'trigger';
1076- }
1077- if ($this->mode & PEAR_ERROR_DIE) {
1078- $modes[] = 'die';
1079- }
1080- if ($this->mode & PEAR_ERROR_RETURN) {
1081- $modes[] = 'return';
1082- }
1083- return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
1084- 'prefix="%s" info="%s"]',
1085- strtolower(get_class($this)), $this->message, $this->code,
1086- implode("|", $modes), $levels[$this->level],
1087- $this->error_message_prefix,
1088- $this->userinfo);
1089- }
1090-
1091- // }}}
1092-}
1093-
1094-/*
1095- * Local Variables:
1096- * mode: php
1097- * tab-width: 4
1098- * c-basic-offset: 4
1099- * End:
1100- */
1101-?>
1+<?php
2+/**
3+ * PEAR, the PHP Extension and Application Repository
4+ *
5+ * PEAR class and PEAR_Error class
6+ *
7+ * PHP versions 4 and 5
8+ *
9+ * LICENSE: This source file is subject to version 3.0 of the PHP license
10+ * that is available through the world-wide-web at the following URI:
11+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
12+ * the PHP License and are unable to obtain it through the web, please
13+ * send a note to license@php.net so we can mail you a copy immediately.
14+ *
15+ * @category pear
16+ * @package PEAR
17+ * @author Sterling Hughes <sterling@php.net>
18+ * @author Stig Bakken <ssb@php.net>
19+ * @author Tomas V.V.Cox <cox@idecnet.com>
20+ * @author Greg Beaver <cellog@php.net>
21+ * @copyright 1997-2006 The PHP Group
22+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
23+ * @version CVS: $Id: PEAR.php,v 1.7 2006/09/29 14:44:16 hsur Exp $
24+ * @link http://pear.php.net/package/PEAR
25+ * @since File available since Release 0.1
26+ */
27+
28+/**#@+
29+ * ERROR constants
30+ */
31+define('PEAR_ERROR_RETURN', 1);
32+define('PEAR_ERROR_PRINT', 2);
33+define('PEAR_ERROR_TRIGGER', 4);
34+define('PEAR_ERROR_DIE', 8);
35+define('PEAR_ERROR_CALLBACK', 16);
36+/**
37+ * WARNING: obsolete
38+ * @deprecated
39+ */
40+define('PEAR_ERROR_EXCEPTION', 32);
41+/**#@-*/
42+define('PEAR_ZE2', (function_exists('version_compare') &&
43+ version_compare(zend_version(), "2-dev", "ge")));
44+
45+if (substr(PHP_OS, 0, 3) == 'WIN') {
46+ define('OS_WINDOWS', true);
47+ define('OS_UNIX', false);
48+ define('PEAR_OS', 'Windows');
49+} else {
50+ define('OS_WINDOWS', false);
51+ define('OS_UNIX', true);
52+ define('PEAR_OS', 'Unix'); // blatant assumption
53+}
54+
55+// instant backwards compatibility
56+if (!defined('PATH_SEPARATOR')) {
57+ if (OS_WINDOWS) {
58+ define('PATH_SEPARATOR', ';');
59+ } else {
60+ define('PATH_SEPARATOR', ':');
61+ }
62+}
63+
64+$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
65+$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
66+$GLOBALS['_PEAR_destructor_object_list'] = array();
67+$GLOBALS['_PEAR_shutdown_funcs'] = array();
68+$GLOBALS['_PEAR_error_handler_stack'] = array();
69+
70+@ini_set('track_errors', true);
71+
72+/**
73+ * Base class for other PEAR classes. Provides rudimentary
74+ * emulation of destructors.
75+ *
76+ * If you want a destructor in your class, inherit PEAR and make a
77+ * destructor method called _yourclassname (same name as the
78+ * constructor, but with a "_" prefix). Also, in your constructor you
79+ * have to call the PEAR constructor: $this->PEAR();.
80+ * The destructor method will be called without parameters. Note that
81+ * at in some SAPI implementations (such as Apache), any output during
82+ * the request shutdown (in which destructors are called) seems to be
83+ * discarded. If you need to get any debug information from your
84+ * destructor, use error_log(), syslog() or something similar.
85+ *
86+ * IMPORTANT! To use the emulated destructors you need to create the
87+ * objects by reference: $obj =& new PEAR_child;
88+ *
89+ * @category pear
90+ * @package PEAR
91+ * @author Stig Bakken <ssb@php.net>
92+ * @author Tomas V.V. Cox <cox@idecnet.com>
93+ * @author Greg Beaver <cellog@php.net>
94+ * @copyright 1997-2006 The PHP Group
95+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
96+ * @version Release: 1.4.9
97+ * @link http://pear.php.net/package/PEAR
98+ * @see PEAR_Error
99+ * @since Class available since PHP 4.0.2
100+ * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
101+ */
102+class PEAR
103+{
104+ // {{{ properties
105+
106+ /**
107+ * Whether to enable internal debug messages.
108+ *
109+ * @var bool
110+ * @access private
111+ */
112+ var $_debug = false;
113+
114+ /**
115+ * Default error mode for this object.
116+ *
117+ * @var int
118+ * @access private
119+ */
120+ var $_default_error_mode = null;
121+
122+ /**
123+ * Default error options used for this object when error mode
124+ * is PEAR_ERROR_TRIGGER.
125+ *
126+ * @var int
127+ * @access private
128+ */
129+ var $_default_error_options = null;
130+
131+ /**
132+ * Default error handler (callback) for this object, if error mode is
133+ * PEAR_ERROR_CALLBACK.
134+ *
135+ * @var string
136+ * @access private
137+ */
138+ var $_default_error_handler = '';
139+
140+ /**
141+ * Which class to use for error objects.
142+ *
143+ * @var string
144+ * @access private
145+ */
146+ var $_error_class = 'PEAR_Error';
147+
148+ /**
149+ * An array of expected errors.
150+ *
151+ * @var array
152+ * @access private
153+ */
154+ var $_expected_errors = array();
155+
156+ // }}}
157+
158+ // {{{ constructor
159+
160+ /**
161+ * Constructor. Registers this object in
162+ * $_PEAR_destructor_object_list for destructor emulation if a
163+ * destructor object exists.
164+ *
165+ * @param string $error_class (optional) which class to use for
166+ * error objects, defaults to PEAR_Error.
167+ * @access public
168+ * @return void
169+ */
170+ function PEAR($error_class = null)
171+ {
172+ $classname = strtolower(get_class($this));
173+ if ($this->_debug) {
174+ print "PEAR constructor called, class=$classname\n";
175+ }
176+ if ($error_class !== null) {
177+ $this->_error_class = $error_class;
178+ }
179+ while ($classname && strcasecmp($classname, "pear")) {
180+ $destructor = "_$classname";
181+ if (method_exists($this, $destructor)) {
182+ global $_PEAR_destructor_object_list;
183+ $_PEAR_destructor_object_list[] = &$this;
184+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
185+ register_shutdown_function("_PEAR_call_destructors");
186+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
187+ }
188+ break;
189+ } else {
190+ $classname = get_parent_class($classname);
191+ }
192+ }
193+ }
194+
195+ // }}}
196+ // {{{ destructor
197+
198+ /**
199+ * Destructor (the emulated type of...). Does nothing right now,
200+ * but is included for forward compatibility, so subclass
201+ * destructors should always call it.
202+ *
203+ * See the note in the class desciption about output from
204+ * destructors.
205+ *
206+ * @access public
207+ * @return void
208+ */
209+ function _PEAR() {
210+ if ($this->_debug) {
211+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
212+ }
213+ }
214+
215+ // }}}
216+ // {{{ getStaticProperty()
217+
218+ /**
219+ * If you have a class that's mostly/entirely static, and you need static
220+ * properties, you can use this method to simulate them. Eg. in your method(s)
221+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
222+ * You MUST use a reference, or they will not persist!
223+ *
224+ * @access public
225+ * @param string $class The calling classname, to prevent clashes
226+ * @param string $var The variable to retrieve.
227+ * @return mixed A reference to the variable. If not set it will be
228+ * auto initialised to NULL.
229+ */
230+ function &getStaticProperty($class, $var)
231+ {
232+ static $properties;
233+ return $properties[$class][$var];
234+ }
235+
236+ // }}}
237+ // {{{ registerShutdownFunc()
238+
239+ /**
240+ * Use this function to register a shutdown method for static
241+ * classes.
242+ *
243+ * @access public
244+ * @param mixed $func The function name (or array of class/method) to call
245+ * @param mixed $args The arguments to pass to the function
246+ * @return void
247+ */
248+ function registerShutdownFunc($func, $args = array())
249+ {
250+ // if we are called statically, there is a potential
251+ // that no shutdown func is registered. Bug #6445
252+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
253+ register_shutdown_function("_PEAR_call_destructors");
254+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
255+ }
256+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
257+ }
258+
259+ // }}}
260+ // {{{ isError()
261+
262+ /**
263+ * Tell whether a value is a PEAR error.
264+ *
265+ * @param mixed $data the value to test
266+ * @param int $code if $data is an error object, return true
267+ * only if $code is a string and
268+ * $obj->getMessage() == $code or
269+ * $code is an integer and $obj->getCode() == $code
270+ * @access public
271+ * @return bool true if parameter is an error
272+ */
273+ function isError($data, $code = null)
274+ {
275+ if (is_a($data, 'PEAR_Error')) {
276+ if (is_null($code)) {
277+ return true;
278+ } elseif (is_string($code)) {
279+ return $data->getMessage() == $code;
280+ } else {
281+ return $data->getCode() == $code;
282+ }
283+ }
284+ return false;
285+ }
286+
287+ // }}}
288+ // {{{ setErrorHandling()
289+
290+ /**
291+ * Sets how errors generated by this object should be handled.
292+ * Can be invoked both in objects and statically. If called
293+ * statically, setErrorHandling sets the default behaviour for all
294+ * PEAR objects. If called in an object, setErrorHandling sets
295+ * the default behaviour for that object.
296+ *
297+ * @param int $mode
298+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
299+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
300+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
301+ *
302+ * @param mixed $options
303+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
304+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
305+ *
306+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
307+ * to be the callback function or method. A callback
308+ * function is a string with the name of the function, a
309+ * callback method is an array of two elements: the element
310+ * at index 0 is the object, and the element at index 1 is
311+ * the name of the method to call in the object.
312+ *
313+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
314+ * a printf format string used when printing the error
315+ * message.
316+ *
317+ * @access public
318+ * @return void
319+ * @see PEAR_ERROR_RETURN
320+ * @see PEAR_ERROR_PRINT
321+ * @see PEAR_ERROR_TRIGGER
322+ * @see PEAR_ERROR_DIE
323+ * @see PEAR_ERROR_CALLBACK
324+ * @see PEAR_ERROR_EXCEPTION
325+ *
326+ * @since PHP 4.0.5
327+ */
328+
329+ function setErrorHandling($mode = null, $options = null)
330+ {
331+ if (isset($this) && is_a($this, 'PEAR')) {
332+ $setmode = &$this->_default_error_mode;
333+ $setoptions = &$this->_default_error_options;
334+ } else {
335+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
336+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
337+ }
338+
339+ switch ($mode) {
340+ case PEAR_ERROR_EXCEPTION:
341+ case PEAR_ERROR_RETURN:
342+ case PEAR_ERROR_PRINT:
343+ case PEAR_ERROR_TRIGGER:
344+ case PEAR_ERROR_DIE:
345+ case null:
346+ $setmode = $mode;
347+ $setoptions = $options;
348+ break;
349+
350+ case PEAR_ERROR_CALLBACK:
351+ $setmode = $mode;
352+ // class/object method callback
353+ if (is_callable($options)) {
354+ $setoptions = $options;
355+ } else {
356+ trigger_error("invalid error callback", E_USER_WARNING);
357+ }
358+ break;
359+
360+ default:
361+ trigger_error("invalid error mode", E_USER_WARNING);
362+ break;
363+ }
364+ }
365+
366+ // }}}
367+ // {{{ expectError()
368+
369+ /**
370+ * This method is used to tell which errors you expect to get.
371+ * Expected errors are always returned with error mode
372+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
373+ * and this method pushes a new element onto it. The list of
374+ * expected errors are in effect until they are popped off the
375+ * stack with the popExpect() method.
376+ *
377+ * Note that this method can not be called statically
378+ *
379+ * @param mixed $code a single error code or an array of error codes to expect
380+ *
381+ * @return int the new depth of the "expected errors" stack
382+ * @access public
383+ */
384+ function expectError($code = '*')
385+ {
386+ if (is_array($code)) {
387+ array_push($this->_expected_errors, $code);
388+ } else {
389+ array_push($this->_expected_errors, array($code));
390+ }
391+ return sizeof($this->_expected_errors);
392+ }
393+
394+ // }}}
395+ // {{{ popExpect()
396+
397+ /**
398+ * This method pops one element off the expected error codes
399+ * stack.
400+ *
401+ * @return array the list of error codes that were popped
402+ */
403+ function popExpect()
404+ {
405+ return array_pop($this->_expected_errors);
406+ }
407+
408+ // }}}
409+ // {{{ _checkDelExpect()
410+
411+ /**
412+ * This method checks unsets an error code if available
413+ *
414+ * @param mixed error code
415+ * @return bool true if the error code was unset, false otherwise
416+ * @access private
417+ * @since PHP 4.3.0
418+ */
419+ function _checkDelExpect($error_code)
420+ {
421+ $deleted = false;
422+
423+ foreach ($this->_expected_errors AS $key => $error_array) {
424+ if (in_array($error_code, $error_array)) {
425+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
426+ $deleted = true;
427+ }
428+
429+ // clean up empty arrays
430+ if (0 == count($this->_expected_errors[$key])) {
431+ unset($this->_expected_errors[$key]);
432+ }
433+ }
434+ return $deleted;
435+ }
436+
437+ // }}}
438+ // {{{ delExpect()
439+
440+ /**
441+ * This method deletes all occurences of the specified element from
442+ * the expected error codes stack.
443+ *
444+ * @param mixed $error_code error code that should be deleted
445+ * @return mixed list of error codes that were deleted or error
446+ * @access public
447+ * @since PHP 4.3.0
448+ */
449+ function delExpect($error_code)
450+ {
451+ $deleted = false;
452+
453+ if ((is_array($error_code) && (0 != count($error_code)))) {
454+ // $error_code is a non-empty array here;
455+ // we walk through it trying to unset all
456+ // values
457+ foreach($error_code as $key => $error) {
458+ if ($this->_checkDelExpect($error)) {
459+ $deleted = true;
460+ } else {
461+ $deleted = false;
462+ }
463+ }
464+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
465+ } elseif (!empty($error_code)) {
466+ // $error_code comes alone, trying to unset it
467+ if ($this->_checkDelExpect($error_code)) {
468+ return true;
469+ } else {
470+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
471+ }
472+ } else {
473+ // $error_code is empty
474+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
475+ }
476+ }
477+
478+ // }}}
479+ // {{{ raiseError()
480+
481+ /**
482+ * This method is a wrapper that returns an instance of the
483+ * configured error class with this object's default error
484+ * handling applied. If the $mode and $options parameters are not
485+ * specified, the object's defaults are used.
486+ *
487+ * @param mixed $message a text error message or a PEAR error object
488+ *
489+ * @param int $code a numeric error code (it is up to your class
490+ * to define these if you want to use codes)
491+ *
492+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
493+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
494+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
495+ *
496+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
497+ * specifies the PHP-internal error level (one of
498+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
499+ * If $mode is PEAR_ERROR_CALLBACK, this
500+ * parameter specifies the callback function or
501+ * method. In other error modes this parameter
502+ * is ignored.
503+ *
504+ * @param string $userinfo If you need to pass along for example debug
505+ * information, this parameter is meant for that.
506+ *
507+ * @param string $error_class The returned error object will be
508+ * instantiated from this class, if specified.
509+ *
510+ * @param bool $skipmsg If true, raiseError will only pass error codes,
511+ * the error message parameter will be dropped.
512+ *
513+ * @access public
514+ * @return object a PEAR error object
515+ * @see PEAR::setErrorHandling
516+ * @since PHP 4.0.5
517+ */
518+ function &raiseError($message = null,
519+ $code = null,
520+ $mode = null,
521+ $options = null,
522+ $userinfo = null,
523+ $error_class = null,
524+ $skipmsg = false)
525+ {
526+ // The error is yet a PEAR error object
527+ if (is_object($message)) {
528+ $code = $message->getCode();
529+ $userinfo = $message->getUserInfo();
530+ $error_class = $message->getType();
531+ $message->error_message_prefix = '';
532+ $message = $message->getMessage();
533+ }
534+
535+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
536+ if ($exp[0] == "*" ||
537+ (is_int(reset($exp)) && in_array($code, $exp)) ||
538+ (is_string(reset($exp)) && in_array($message, $exp))) {
539+ $mode = PEAR_ERROR_RETURN;
540+ }
541+ }
542+ // No mode given, try global ones
543+ if ($mode === null) {
544+ // Class error handler
545+ if (isset($this) && isset($this->_default_error_mode)) {
546+ $mode = $this->_default_error_mode;
547+ $options = $this->_default_error_options;
548+ // Global error handler
549+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
550+ $mode = $GLOBALS['_PEAR_default_error_mode'];
551+ $options = $GLOBALS['_PEAR_default_error_options'];
552+ }
553+ }
554+
555+ if ($error_class !== null) {
556+ $ec = $error_class;
557+ } elseif (isset($this) && isset($this->_error_class)) {
558+ $ec = $this->_error_class;
559+ } else {
560+ $ec = 'PEAR_Error';
561+ }
562+ if ($skipmsg) {
563+ $a = &new $ec($code, $mode, $options, $userinfo);
564+ return $a;
565+ } else {
566+ $a = &new $ec($message, $code, $mode, $options, $userinfo);
567+ return $a;
568+ }
569+ }
570+
571+ // }}}
572+ // {{{ throwError()
573+
574+ /**
575+ * Simpler form of raiseError with fewer options. In most cases
576+ * message, code and userinfo are enough.
577+ *
578+ * @param string $message
579+ *
580+ */
581+ function &throwError($message = null,
582+ $code = null,
583+ $userinfo = null)
584+ {
585+ if (isset($this) && is_a($this, 'PEAR')) {
586+ $a = &$this->raiseError($message, $code, null, null, $userinfo);
587+ return $a;
588+ } else {
589+ $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
590+ return $a;
591+ }
592+ }
593+
594+ // }}}
595+ function staticPushErrorHandling($mode, $options = null)
596+ {
597+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
598+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
599+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
600+ $stack[] = array($def_mode, $def_options);
601+ switch ($mode) {
602+ case PEAR_ERROR_EXCEPTION:
603+ case PEAR_ERROR_RETURN:
604+ case PEAR_ERROR_PRINT:
605+ case PEAR_ERROR_TRIGGER:
606+ case PEAR_ERROR_DIE:
607+ case null:
608+ $def_mode = $mode;
609+ $def_options = $options;
610+ break;
611+
612+ case PEAR_ERROR_CALLBACK:
613+ $def_mode = $mode;
614+ // class/object method callback
615+ if (is_callable($options)) {
616+ $def_options = $options;
617+ } else {
618+ trigger_error("invalid error callback", E_USER_WARNING);
619+ }
620+ break;
621+
622+ default:
623+ trigger_error("invalid error mode", E_USER_WARNING);
624+ break;
625+ }
626+ $stack[] = array($mode, $options);
627+ return true;
628+ }
629+
630+ function staticPopErrorHandling()
631+ {
632+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
633+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
634+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
635+ array_pop($stack);
636+ list($mode, $options) = $stack[sizeof($stack) - 1];
637+ array_pop($stack);
638+ switch ($mode) {
639+ case PEAR_ERROR_EXCEPTION:
640+ case PEAR_ERROR_RETURN:
641+ case PEAR_ERROR_PRINT:
642+ case PEAR_ERROR_TRIGGER:
643+ case PEAR_ERROR_DIE:
644+ case null:
645+ $setmode = $mode;
646+ $setoptions = $options;
647+ break;
648+
649+ case PEAR_ERROR_CALLBACK:
650+ $setmode = $mode;
651+ // class/object method callback
652+ if (is_callable($options)) {
653+ $setoptions = $options;
654+ } else {
655+ trigger_error("invalid error callback", E_USER_WARNING);
656+ }
657+ break;
658+
659+ default:
660+ trigger_error("invalid error mode", E_USER_WARNING);
661+ break;
662+ }
663+ return true;
664+ }
665+
666+ // {{{ pushErrorHandling()
667+
668+ /**
669+ * Push a new error handler on top of the error handler options stack. With this
670+ * you can easily override the actual error handler for some code and restore
671+ * it later with popErrorHandling.
672+ *
673+ * @param mixed $mode (same as setErrorHandling)
674+ * @param mixed $options (same as setErrorHandling)
675+ *
676+ * @return bool Always true
677+ *
678+ * @see PEAR::setErrorHandling
679+ */
680+ function pushErrorHandling($mode, $options = null)
681+ {
682+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
683+ if (isset($this) && is_a($this, 'PEAR')) {
684+ $def_mode = &$this->_default_error_mode;
685+ $def_options = &$this->_default_error_options;
686+ } else {
687+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
688+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
689+ }
690+ $stack[] = array($def_mode, $def_options);
691+
692+ if (isset($this) && is_a($this, 'PEAR')) {
693+ $this->setErrorHandling($mode, $options);
694+ } else {
695+ PEAR::setErrorHandling($mode, $options);
696+ }
697+ $stack[] = array($mode, $options);
698+ return true;
699+ }
700+
701+ // }}}
702+ // {{{ popErrorHandling()
703+
704+ /**
705+ * Pop the last error handler used
706+ *
707+ * @return bool Always true
708+ *
709+ * @see PEAR::pushErrorHandling
710+ */
711+ function popErrorHandling()
712+ {
713+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
714+ array_pop($stack);
715+ list($mode, $options) = $stack[sizeof($stack) - 1];
716+ array_pop($stack);
717+ if (isset($this) && is_a($this, 'PEAR')) {
718+ $this->setErrorHandling($mode, $options);
719+ } else {
720+ PEAR::setErrorHandling($mode, $options);
721+ }
722+ return true;
723+ }
724+
725+ // }}}
726+ // {{{ loadExtension()
727+
728+ /**
729+ * OS independant PHP extension load. Remember to take care
730+ * on the correct extension name for case sensitive OSes.
731+ *
732+ * @param string $ext The extension name
733+ * @return bool Success or not on the dl() call
734+ */
735+ function loadExtension($ext)
736+ {
737+ if (!extension_loaded($ext)) {
738+ // if either returns true dl() will produce a FATAL error, stop that
739+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
740+ return false;
741+ }
742+ if (OS_WINDOWS) {
743+ $suffix = '.dll';
744+ } elseif (PHP_OS == 'HP-UX') {
745+ $suffix = '.sl';
746+ } elseif (PHP_OS == 'AIX') {
747+ $suffix = '.a';
748+ } elseif (PHP_OS == 'OSX') {
749+ $suffix = '.bundle';
750+ } else {
751+ $suffix = '.so';
752+ }
753+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
754+ }
755+ return true;
756+ }
757+
758+ // }}}
759+}
760+
761+// {{{ _PEAR_call_destructors()
762+
763+function _PEAR_call_destructors()
764+{
765+ global $_PEAR_destructor_object_list;
766+ if (is_array($_PEAR_destructor_object_list) &&
767+ sizeof($_PEAR_destructor_object_list))
768+ {
769+ reset($_PEAR_destructor_object_list);
770+ if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
771+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
772+ }
773+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
774+ $classname = get_class($objref);
775+ while ($classname) {
776+ $destructor = "_$classname";
777+ if (method_exists($objref, $destructor)) {
778+ $objref->$destructor();
779+ break;
780+ } else {
781+ $classname = get_parent_class($classname);
782+ }
783+ }
784+ }
785+ // Empty the object list to ensure that destructors are
786+ // not called more than once.
787+ $_PEAR_destructor_object_list = array();
788+ }
789+
790+ // Now call the shutdown functions
791+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
792+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
793+ call_user_func_array($value[0], $value[1]);
794+ }
795+ }
796+}
797+
798+// }}}
799+/**
800+ * Standard PEAR error class for PHP 4
801+ *
802+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
803+ *
804+ * @category pear
805+ * @package PEAR
806+ * @author Stig Bakken <ssb@php.net>
807+ * @author Tomas V.V. Cox <cox@idecnet.com>
808+ * @author Gregory Beaver <cellog@php.net>
809+ * @copyright 1997-2006 The PHP Group
810+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
811+ * @version Release: 1.4.9
812+ * @link http://pear.php.net/manual/en/core.pear.pear-error.php
813+ * @see PEAR::raiseError(), PEAR::throwError()
814+ * @since Class available since PHP 4.0.2
815+ */
816+class PEAR_Error
817+{
818+ // {{{ properties
819+
820+ var $error_message_prefix = '';
821+ var $mode = PEAR_ERROR_RETURN;
822+ var $level = E_USER_NOTICE;
823+ var $code = -1;
824+ var $message = '';
825+ var $userinfo = '';
826+ var $backtrace = null;
827+
828+ // }}}
829+ // {{{ constructor
830+
831+ /**
832+ * PEAR_Error constructor
833+ *
834+ * @param string $message message
835+ *
836+ * @param int $code (optional) error code
837+ *
838+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
839+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
840+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
841+ *
842+ * @param mixed $options (optional) error level, _OR_ in the case of
843+ * PEAR_ERROR_CALLBACK, the callback function or object/method
844+ * tuple.
845+ *
846+ * @param string $userinfo (optional) additional user/debug info
847+ *
848+ * @access public
849+ *
850+ */
851+ function PEAR_Error($message = 'unknown error', $code = null,
852+ $mode = null, $options = null, $userinfo = null)
853+ {
854+ if ($mode === null) {
855+ $mode = PEAR_ERROR_RETURN;
856+ }
857+ $this->message = $message;
858+ $this->code = $code;
859+ $this->mode = $mode;
860+ $this->userinfo = $userinfo;
861+ if (function_exists("debug_backtrace")) {
862+ if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
863+ $this->backtrace = debug_backtrace();
864+ }
865+ }
866+ if ($mode & PEAR_ERROR_CALLBACK) {
867+ $this->level = E_USER_NOTICE;
868+ $this->callback = $options;
869+ } else {
870+ if ($options === null) {
871+ $options = E_USER_NOTICE;
872+ }
873+ $this->level = $options;
874+ $this->callback = null;
875+ }
876+ if ($this->mode & PEAR_ERROR_PRINT) {
877+ if (is_null($options) || is_int($options)) {
878+ $format = "%s";
879+ } else {
880+ $format = $options;
881+ }
882+ printf($format, $this->getMessage());
883+ }
884+ if ($this->mode & PEAR_ERROR_TRIGGER) {
885+ trigger_error($this->getMessage(), $this->level);
886+ }
887+ if ($this->mode & PEAR_ERROR_DIE) {
888+ $msg = $this->getMessage();
889+ if (is_null($options) || is_int($options)) {
890+ $format = "%s";
891+ if (substr($msg, -1) != "\n") {
892+ $msg .= "\n";
893+ }
894+ } else {
895+ $format = $options;
896+ }
897+ die(sprintf($format, $msg));
898+ }
899+ if ($this->mode & PEAR_ERROR_CALLBACK) {
900+ if (is_callable($this->callback)) {
901+ call_user_func($this->callback, $this);
902+ }
903+ }
904+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
905+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
906+ eval('$e = new Exception($this->message, $this->code);throw($e);');
907+ }
908+ }
909+
910+ // }}}
911+ // {{{ getMode()
912+
913+ /**
914+ * Get the error mode from an error object.
915+ *
916+ * @return int error mode
917+ * @access public
918+ */
919+ function getMode() {
920+ return $this->mode;
921+ }
922+
923+ // }}}
924+ // {{{ getCallback()
925+
926+ /**
927+ * Get the callback function/method from an error object.
928+ *
929+ * @return mixed callback function or object/method array
930+ * @access public
931+ */
932+ function getCallback() {
933+ return $this->callback;
934+ }
935+
936+ // }}}
937+ // {{{ getMessage()
938+
939+
940+ /**
941+ * Get the error message from an error object.
942+ *
943+ * @return string full error message
944+ * @access public
945+ */
946+ function getMessage()
947+ {
948+ return ($this->error_message_prefix . $this->message);
949+ }
950+
951+
952+ // }}}
953+ // {{{ getCode()
954+
955+ /**
956+ * Get error code from an error object
957+ *
958+ * @return int error code
959+ * @access public
960+ */
961+ function getCode()
962+ {
963+ return $this->code;
964+ }
965+
966+ // }}}
967+ // {{{ getType()
968+
969+ /**
970+ * Get the name of this error/exception.
971+ *
972+ * @return string error/exception name (type)
973+ * @access public
974+ */
975+ function getType()
976+ {
977+ return get_class($this);
978+ }
979+
980+ // }}}
981+ // {{{ getUserInfo()
982+
983+ /**
984+ * Get additional user-supplied information.
985+ *
986+ * @return string user-supplied information
987+ * @access public
988+ */
989+ function getUserInfo()
990+ {
991+ return $this->userinfo;
992+ }
993+
994+ // }}}
995+ // {{{ getDebugInfo()
996+
997+ /**
998+ * Get additional debug information supplied by the application.
999+ *
1000+ * @return string debug information
1001+ * @access public
1002+ */
1003+ function getDebugInfo()
1004+ {
1005+ return $this->getUserInfo();
1006+ }
1007+
1008+ // }}}
1009+ // {{{ getBacktrace()
1010+
1011+ /**
1012+ * Get the call backtrace from where the error was generated.
1013+ * Supported with PHP 4.3.0 or newer.
1014+ *
1015+ * @param int $frame (optional) what frame to fetch
1016+ * @return array Backtrace, or NULL if not available.
1017+ * @access public
1018+ */
1019+ function getBacktrace($frame = null)
1020+ {
1021+ if (defined('PEAR_IGNORE_BACKTRACE')) {
1022+ return null;
1023+ }
1024+ if ($frame === null) {
1025+ return $this->backtrace;
1026+ }
1027+ return $this->backtrace[$frame];
1028+ }
1029+
1030+ // }}}
1031+ // {{{ addUserInfo()
1032+
1033+ function addUserInfo($info)
1034+ {
1035+ if (empty($this->userinfo)) {
1036+ $this->userinfo = $info;
1037+ } else {
1038+ $this->userinfo .= " ** $info";
1039+ }
1040+ }
1041+
1042+ // }}}
1043+ // {{{ toString()
1044+
1045+ /**
1046+ * Make a string representation of this object.
1047+ *
1048+ * @return string a string with an object summary
1049+ * @access public
1050+ */
1051+ function toString() {
1052+ $modes = array();
1053+ $levels = array(E_USER_NOTICE => 'notice',
1054+ E_USER_WARNING => 'warning',
1055+ E_USER_ERROR => 'error');
1056+ if ($this->mode & PEAR_ERROR_CALLBACK) {
1057+ if (is_array($this->callback)) {
1058+ $callback = (is_object($this->callback[0]) ?
1059+ strtolower(get_class($this->callback[0])) :
1060+ $this->callback[0]) . '::' .
1061+ $this->callback[1];
1062+ } else {
1063+ $callback = $this->callback;
1064+ }
1065+ return sprintf('[%s: message="%s" code=%d mode=callback '.
1066+ 'callback=%s prefix="%s" info="%s"]',
1067+ strtolower(get_class($this)), $this->message, $this->code,
1068+ $callback, $this->error_message_prefix,
1069+ $this->userinfo);
1070+ }
1071+ if ($this->mode & PEAR_ERROR_PRINT) {
1072+ $modes[] = 'print';
1073+ }
1074+ if ($this->mode & PEAR_ERROR_TRIGGER) {
1075+ $modes[] = 'trigger';
1076+ }
1077+ if ($this->mode & PEAR_ERROR_DIE) {
1078+ $modes[] = 'die';
1079+ }
1080+ if ($this->mode & PEAR_ERROR_RETURN) {
1081+ $modes[] = 'return';
1082+ }
1083+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
1084+ 'prefix="%s" info="%s"]',
1085+ strtolower(get_class($this)), $this->message, $this->code,
1086+ implode("|", $modes), $levels[$this->level],
1087+ $this->error_message_prefix,
1088+ $this->userinfo);
1089+ }
1090+
1091+ // }}}
1092+}
1093+
1094+/*
1095+ * Local Variables:
1096+ * mode: php
1097+ * tab-width: 4
1098+ * c-basic-offset: 4
1099+ * End:
1100+ */
1101+?>
--- a/trunk/NP_Moblog/sharedlibs/cles/Feedback.php
+++ b/trunk/NP_Moblog/sharedlibs/cles/Feedback.php
@@ -1,176 +1,178 @@
1-<?php
2-// vim: tabstop=2:shiftwidth=2
3-
4-/**
5- * Feedback.php ($Revision: 1.1 $)
6- *
7- * by hsur ( http://blog.cles.jp/np_cles )
8- * $Id: Feedback.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
9-*/
10-
11-/*
12- * Copyright (C) 2006 CLES. All rights reserved.
13- *
14- * This program is free software; you can redistribute it and/or
15- * modify it under the terms of the GNU General Public License
16- * as published by the Free Software Foundation; either version 2
17- * of the License, or (at your option) any later version.
18- *
19- * This program is distributed in the hope that it will be useful,
20- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22- * GNU General Public License for more details.
23- *
24- * You should have received a copy of the GNU General Public License
25- * along with this program; if not, write to the Free Software
26- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27- *
28- * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29- * permission to link the code of this program with those files in the PEAR
30- * library that are licensed under the PHP License (or with modified versions
31- * of those files that use the same license as those files), and distribute
32- * linked combinations including the two. You must obey the GNU General Public
33- * License in all respects for all of the code used other than those files in
34- * the PEAR library that are licensed under the PHP License. If you modify
35- * this file, you may extend this exception to your version of the file,
36- * but you are not obligated to do so. If you do not wish to do so, delete
37- * this exception statement from your version.
38-*/
39-
40-class cles_Feedback {
41- var $oPluginAdmin;
42- function CLES_Feedback(&$pluginAdmin){
43- $this->oPluginAdmin = $pluginAdmin;
44- }
45-
46- function getMenuStr(){
47- return mb_convert_encoding('動作確認/不具合報告', _CHARSET, 'UTF-8');
48- }
49-
50- function printForm($extra = '') {
51- ob_start();
52-
53- global $nucleus, $CONF;
54-
55- echo "<h2>動作確認/不具合報告</h2>";
56- echo '<p>下記より、作者への動作確認/不具合の報告を行うことができます。</p>';
57-
58- // js
59- echo '<script langage="JavaScript">
60- function selectall(){
61- var elements = document.getElementsByTagName(\'input\');
62- for( var i=0; i < elements.length; i++){
63- var e = elements[i];
64- if( e.type == \'checkbox\' ){
65- e.checked = true;
66- }
67- }
68- return false;
69- }
70- </script>';
71-
72- echo "<h3>収集する情報と公開について</h3>";
73- echo '<p>デフォルトで必要最低限の環境情報(赤字のもの)を開発者のサーバへ送信します。<br />
74- <span style="font-weight:bold; color:red">差し支えない範囲で環境情報の提供にご協力ください。</span></p>
75- <p>※ 収集した情報は統計処理、及びプラグインのBugFixのみに利用されます。また統計処理した結果については公表することがあります。</p>';
76- echo '<p><a href="#" onclick="javascript:selectall();return false;">全て送信する場合はここをクリック</a></p>';
77-
78- echo "<h3>サイト固有コードについて</h3>";
79- echo '<p>動作報告の重複を取り除くため、管理画面のURLのmd5を計算したものを送信しています。この情報から管理画面のURLを復元することはできないようになっています。<a href="http://computers.yahoo.co.jp/dict/security/hash/677.html" target="_blank">md5の解説についてはこちらをご覧ください。(Yahoo!コンピュータ用語辞典)</a></p>';
80-
81- // form
82- echo '<form method="post" action="http://blog.cles.jp/support/report.php">' . "\n";
83-
84- // table
85- echo "<table>\n";
86- echo "<tr>\n";
87- echo "<th>項目の説明</th>\n";
88- echo "<th>送信される値</th>\n";
89- echo "<th><a href=\"#\" onclick=\"javascript:selectall();return false;\">全て送信する</th>\n";
90- echo "</tr>\n";
91-
92- $res = sql_query("show variables like 'version'");
93- $assoc = mysql_fetch_assoc($res);
94- $mysqlVersion = $assoc['Value'];
95-
96- if( function_exists('gd_info') )
97- $gdinfo = @gd_info();
98- else
99- $gdinfo['GD Version'] = 'GD is not supported';
100-
101- global $CONF;
102-
103- $this->_printtr('siteid', 'サイトの固有コード', md5(trim($CONF['AdminURL'])));
104- $this->_printtr('plugin_name', 'プラグイン名', $this->oPluginAdmin->plugin->getName());
105- $this->_printtr('plugin_version', 'プラグインのバージョン', $this->oPluginAdmin->plugin->getVersion());
106- $this->_printtr('plugin_info', 'プラグインの情報', $extra, true);
107- $this->_printtr('nucleus_version', 'Nucleusのバージョン', $nucleus['version'], true);
108- $this->_printtr('nucleus_charset', 'Nucleusのキャラクタセット', _CHARSET);
109- $this->_printtr('php_version', 'PHPのバージョン', PHP_VERSION, true);
110- $this->_printtr('php_sapi', 'PHPの種類', php_sapi_name());
111- $this->_printtr('php_os', 'OSの種類', PHP_OS, true);
112- $this->_printtr('php_safemode', 'セーフモードの有無', ini_get('safe_mode') ? 'on' : 'off');
113- $this->_printtr('php_gd_version', 'GDのバージョン', $gdinfo['GD Version'], true);
114- $this->_printtr('php_gd_support', 'サポートしているイメージタイプ', implode(',', $this->_supportedImageTypes()) );
115- $this->_printtr('mysql_version', 'MySQLのバージョン', $mysqlVersion, true);
116-
117- echo "<tr>\n";
118- echo "<td>このプラグインは機能しましたか?</td>\n";
119- echo '<td colsan="2"><input type="radio" name="user_intention" value="ok" />はい <br/> <input type="radio" name="intention" value="ng" />いいえ'."</td>\n";
120- echo "</tr>\n";
121-
122- echo "<tr>\n";
123- 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";
124- echo '<td colspan="2"><textarea name="user_freetext" rows="10" cols="70"></textarea>'."</td>\n";
125- echo "</tr>\n";
126-
127- echo "<tr>\n";
128- echo "<td>よろしければサイトのURLを教えてください</td>\n";
129- echo '<td colspan="2"><textarea name="user_url" rows="1" cols="70"></textarea>'."</td>\n";
130- echo "</tr>\n";
131-
132- echo "<tr>\n";
133- echo "<td>リンク集作成の際、リンクをはらせていただけますか?</td>\n";
134- echo '<td colspan="2"><input type="radio" name="user_disclose" value="yes" />はい <br/> <input type="radio" name="intention" value="no" />いいえ'."</td>\n";
135- echo "</tr>\n";
136-
137- echo '<tr><td colspan="3"><div align="right"><input type="submit" name="submit" value="動作確認を送信する" /></div></td></tr>';
138- echo "</table>\n";
139- echo "</form>\n";
140-
141- $contents = ob_get_contents();
142- ob_end_clean();
143- echo mb_convert_encoding($contents, _CHARSET, 'UTF-8');
144- }
145-
146- function _printtr($name, $desc, $value, $canDisable = false) {
147- echo "<tr>\n";
148-
149- if ($canDisable) {
150- echo "<td>".$desc."</td>\n";
151- echo "<td>".htmlspecialchars($value)."</td>\n";
152- echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" /></td>'."\n";
153- } else {
154- echo '<td><span style="font-weight:bold; color:red">'.$desc."</span></td>\n";
155- echo '<td><span style="font-weight:bold; color:red">'.htmlspecialchars($value)."</span></td>\n";
156- echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" readonly="readonly" checked="checked"/></span></td>'."\n";
157- }
158- echo "</tr>\n";
159- }
160-
161- function _supportedImageTypes() {
162- if( !function_exists('gd_info') ) return "";
163-
164- $aSupportedTypes = array ();
165- $aPossibleImageTypeBits = array (IMG_GIF => 'GIF', IMG_JPG => 'JPG', IMG_PNG => 'PNG', IMG_WBMP => 'WBMP');
166-
167- foreach ($aPossibleImageTypeBits as $iImageTypeBits => $sImageTypeString) {
168- if (imagetypes() & $iImageTypeBits) {
169- $aSupportedTypes[] = $sImageTypeString;
170- }
171- }
172-
173- return $aSupportedTypes;
174- }
175-
176-}
1+<?php
2+// vim: tabstop=2:shiftwidth=2
3+
4+/**
5+ * Feedback.php ($Revision: 1.30 $)
6+ *
7+ * by hsur ( http://blog.cles.jp/np_cles )
8+ * $Id: Feedback.php,v 1.30 2008/05/18 07:01:25 hsur Exp $
9+*/
10+
11+/*
12+ * Copyright (C) 2006 CLES. All rights reserved.
13+ *
14+ * This program is free software; you can redistribute it and/or
15+ * modify it under the terms of the GNU General Public License
16+ * as published by the Free Software Foundation; either version 2
17+ * of the License, or (at your option) any later version.
18+ *
19+ * This program is distributed in the hope that it will be useful,
20+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+ * GNU General Public License for more details.
23+ *
24+ * You should have received a copy of the GNU General Public License
25+ * along with this program; if not, write to the Free Software
26+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27+ *
28+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29+ * permission to link the code of this program with those files in the PEAR
30+ * library that are licensed under the PHP License (or with modified versions
31+ * of those files that use the same license as those files), and distribute
32+ * linked combinations including the two. You must obey the GNU General Public
33+ * License in all respects for all of the code used other than those files in
34+ * the PEAR library that are licensed under the PHP License. If you modify
35+ * this file, you may extend this exception to your version of the file,
36+ * but you are not obligated to do so. If you do not wish to do so, delete
37+ * this exception statement from your version.
38+*/
39+
40+class cles_Feedback {
41+ var $oPluginAdmin;
42+ function CLES_Feedback(&$pluginAdmin){
43+ $this->oPluginAdmin = $pluginAdmin;
44+ }
45+
46+ function getMenuStr(){
47+ return mb_convert_encoding('動作確認/不具合報告', _CHARSET, 'UTF-8');
48+ }
49+
50+ function printForm($extra = '') {
51+ ob_start();
52+
53+ global $nucleus, $CONF;
54+
55+ echo "<h2>動作確認/不具合報告</h2>";
56+ echo '<p>下記より、作者への動作確認/不具合の報告を行うことができます。</p>';
57+
58+ // js
59+ echo '<script langage="JavaScript">
60+//<![CDATA[
61+function selectall(){
62+ var elements = document.getElementsByTagName(\'input\');
63+ for( var i=0; i < elements.length; i++){
64+ var e = elements[i];
65+ if( e.type == \'checkbox\' ){
66+ e.checked = true;
67+ }
68+ }
69+ return false;
70+}
71+//]]>
72+</script>';
73+
74+ echo "<h3>収集する情報と公開について</h3>";
75+ echo '<p>デフォルトで必要最低限の環境情報(赤字のもの)を開発者のサーバへ送信します。<br />
76+ <span style="font-weight:bold; color:red">差し支えない範囲で環境情報の提供にご協力ください。</span></p>
77+ <p>※ 収集した情報は統計処理、及びプラグインのBugFixのみに利用されます。また統計処理した結果については公表することがあります。</p>';
78+ echo '<p><a href="#" onclick="javascript:selectall();return false;">全て送信する場合はここをクリック</a></p>';
79+
80+ echo "<h3>サイト固有コードについて</h3>";
81+ echo '<p>動作報告の重複を取り除くため、管理画面のURLのmd5を計算したものを送信しています。この情報から管理画面のURLを復元することはできないようになっています。<a href="http://e-words.jp/w/MD5.html" target="_blank">md5の解説についてはこちらをご覧ください。(e-WordsIT用語辞典)</a></p>';
82+
83+ // form
84+ echo '<form method="post" action="http://blog.cles.jp/support/report.php">' . "\n";
85+
86+ // table
87+ echo "<table>\n";
88+ echo "<tr>\n";
89+ echo "<th>項目の説明</th>\n";
90+ echo "<th>送信される値</th>\n";
91+ echo "<th><a href=\"#\" onclick=\"javascript:selectall();return false;\">全てチェック</a></th>\n";
92+ echo "</tr>\n";
93+
94+ $res = sql_query("show variables like 'version'");
95+ $assoc = mysql_fetch_assoc($res);
96+ $mysqlVersion = $assoc['Value'];
97+
98+ if( function_exists('gd_info') )
99+ $gdinfo = @gd_info();
100+ else
101+ $gdinfo['GD Version'] = 'GD is not supported';
102+
103+ global $CONF;
104+
105+ $this->_printtr('siteid', 'サイトの固有コード', md5(trim($CONF['AdminURL'])));
106+ $this->_printtr('plugin_name', 'プラグイン名', $this->oPluginAdmin->plugin->getName());
107+ $this->_printtr('plugin_version', 'プラグインのバージョン', $this->oPluginAdmin->plugin->getVersion());
108+ $this->_printtr('plugin_info', '追加情報', $extra, true);
109+ $this->_printtr('nucleus_version', 'Nucleusのバージョン', $nucleus['version'], true);
110+ $this->_printtr('nucleus_charset', 'Nucleusのキャラクタセット', _CHARSET);
111+ $this->_printtr('php_version', 'PHPのバージョン', PHP_VERSION, true);
112+ $this->_printtr('php_sapi', 'PHPの種類', php_sapi_name());
113+ $this->_printtr('php_os', 'OSの種類', PHP_OS, true);
114+ $this->_printtr('php_safemode', 'セーフモードの有無', ini_get('safe_mode') ? 'on' : 'off');
115+ $this->_printtr('php_gd_version', 'GDのバージョン', $gdinfo['GD Version'], true);
116+ $this->_printtr('php_gd_support', 'サポートしているイメージタイプ', implode(',', $this->_supportedImageTypes()) );
117+ $this->_printtr('mysql_version', 'MySQLのバージョン', $mysqlVersion, true);
118+
119+ echo "<tr>\n";
120+ echo "<td colspan=\"2\">このプラグインは機能しましたか?</td>\n";
121+ echo '<td><input type="radio" name="user_intention" value="ok" />はい <br/> <input type="radio" name="intention" value="ng" />いいえ'."</td>\n";
122+ echo "</tr>\n";
123+
124+ echo "<tr>\n";
125+ 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";
126+ echo '<td colspan="2"><textarea name="user_freetext" rows="10" cols="70"></textarea>'."</td>\n";
127+ echo "</tr>\n";
128+
129+ echo "<tr>\n";
130+ echo "<td>よろしければサイトのURLを教えてください</td>\n";
131+ echo '<td colspan="2"><textarea name="user_url" rows="1" cols="70"></textarea>'."</td>\n";
132+ echo "</tr>\n";
133+
134+ echo "<tr>\n";
135+ echo "<td colspan=\"2\">リンク集作成の際、リンクをはらせていただけますか?</td>\n";
136+ echo '<td><input type="radio" name="user_disclose" value="yes" />はい <br/> <input type="radio" name="intention" value="no" />いいえ'."</td>\n";
137+ echo "</tr>\n";
138+
139+ 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>';
140+ echo "</table>\n";
141+ echo "</form>\n";
142+
143+ $contents = ob_get_contents();
144+ ob_end_clean();
145+ echo mb_convert_encoding($contents, _CHARSET, 'UTF-8');
146+ }
147+
148+ function _printtr($name, $desc, $value, $canDisable = false) {
149+ echo "<tr>\n";
150+
151+ if ($canDisable) {
152+ echo "<td>".$desc."</td>\n";
153+ echo "<td>".htmlspecialchars($value)."</td>\n";
154+ echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" /></td>'."\n";
155+ } else {
156+ echo '<td><span style="font-weight:bold; color:red">'.$desc."</span></td>\n";
157+ echo '<td><span style="font-weight:bold; color:red">'.htmlspecialchars($value)."</span></td>\n";
158+ echo '<td>必須<input type="hidden" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" readonly="readonly" checked="checked"/></td>'."\n";
159+ }
160+ echo "</tr>\n";
161+ }
162+
163+ function _supportedImageTypes() {
164+ if( !function_exists('gd_info') ) return "";
165+
166+ $aSupportedTypes = array ();
167+ $aPossibleImageTypeBits = array (IMG_GIF => 'GIF', IMG_JPG => 'JPG', IMG_PNG => 'PNG', IMG_WBMP => 'WBMP');
168+
169+ foreach ($aPossibleImageTypeBits as $iImageTypeBits => $sImageTypeString) {
170+ if (imagetypes() & $iImageTypeBits) {
171+ $aSupportedTypes[] = $sImageTypeString;
172+ }
173+ }
174+
175+ return $aSupportedTypes;
176+ }
177+
178+}
--- a/trunk/NP_Moblog/sharedlibs/cles/Template.php
+++ b/trunk/NP_Moblog/sharedlibs/cles/Template.php
@@ -1,76 +1,87 @@
1-<?php
2-// vim: tabstop=2:shiftwidth=2
3-
4-/**
5- * Template.php ($Revision: 1.1 $)
6- *
7- * by hsur ( http://blog.cles.jp/np_cles )
8- * $Id: Template.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
9-*/
10-
11-/*
12- * Copyright (C) 2006 CLES. All rights reserved.
13- *
14- * This program is free software; you can redistribute it and/or
15- * modify it under the terms of the GNU General Public License
16- * as published by the Free Software Foundation; either version 2
17- * of the License, or (at your option) any later version.
18- *
19- * This program is distributed in the hope that it will be useful,
20- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22- * GNU General Public License for more details.
23- *
24- * You should have received a copy of the GNU General Public License
25- * along with this program; if not, write to the Free Software
26- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27- *
28- * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29- * permission to link the code of this program with those files in the PEAR
30- * library that are licensed under the PHP License (or with modified versions
31- * of those files that use the same license as those files), and distribute
32- * linked combinations including the two. You must obey the GNU General Public
33- * License in all respects for all of the code used other than those files in
34- * the PEAR library that are licensed under the PHP License. If you modify
35- * this file, you may extend this exception to your version of the file,
36- * but you are not obligated to do so. If you do not wish to do so, delete
37- * this exception statement from your version.
38-*/
39-
40-class cles_Template {
41- var $defaultLang = 'japanese-utf8';
42- var $defalutPattern = '#{{(.*?)(\|)?}}#ie';
43- var $lang;
44- var $templateDir;
45-
46- function cles_Template($templateDir) {
47- global $CONF;
48- $this->templateDir = $templateDir;
49- $this->lang = ereg_replace( '[\\|/]', '', getLanguageName());
50- }
51-
52- function fetch($name, $dir = null, $suffix = 'html') {
53- $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->lang.( $suffix ? '.'.strtolower($suffix) : '' );
54- if ( ! file_exists($path) ){
55- $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->defaultLang.( $suffix ? '.'.strtolower($suffix) : '' );
56- if ( ! file_exists($path) )
57- return '';
58- }
59-
60- $fsize = filesize($path);
61- if ($fsize <= 0) return '';
62-
63- $fd = fopen($path, 'r');
64- $contents = fread($fd, $fsize);
65- fclose($fd);
66- return $contents;
67- }
68-
69- function fill($template, $values, $default = null) {
70- if( $default )
71- return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : $default', $template);
72- if( $default === null )
73- return preg_replace($this->defalutPattern, '("$2") ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]', $template);
74- return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : "{{$1}}" ', $template);
75- }
76-}
1+<?php
2+// vim: tabstop=2:shiftwidth=2
3+
4+/**
5+ * Template.php ($Revision: 1.11 $)
6+ *
7+ * by hsur ( http://blog.cles.jp/np_cles )
8+ * $Id: Template.php,v 1.11 2008/08/15 21:17:02 hsur Exp $
9+*/
10+
11+/*
12+ * Copyright (C) 2006-2008 CLES. All rights reserved.
13+ *
14+ * This program is free software; you can redistribute it and/or
15+ * modify it under the terms of the GNU General Public License
16+ * as published by the Free Software Foundation; either version 2
17+ * of the License, or (at your option) any later version.
18+ *
19+ * This program is distributed in the hope that it will be useful,
20+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+ * GNU General Public License for more details.
23+ *
24+ * You should have received a copy of the GNU General Public License
25+ * along with this program; if not, write to the Free Software
26+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27+ *
28+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29+ * permission to link the code of this program with those files in the PEAR
30+ * library that are licensed under the PHP License (or with modified versions
31+ * of those files that use the same license as those files), and distribute
32+ * linked combinations including the two. You must obey the GNU General Public
33+ * License in all respects for all of the code used other than those files in
34+ * the PEAR library that are licensed under the PHP License. If you modify
35+ * this file, you may extend this exception to your version of the file,
36+ * but you are not obligated to do so. If you do not wish to do so, delete
37+ * this exception statement from your version.
38+*/
39+
40+if (!function_exists('getLanguageName')){
41+ function getLanguageName(){
42+ return 'japanese-utf8';
43+ }
44+}
45+
46+class cles_Template {
47+ var $defaultLang = 'japanese-utf8';
48+ var $defalutPattern = '#{{(.*?)(\|)?}}#ie';
49+ var $lang;
50+ var $templateDir;
51+
52+ function cles_Template($templateDir) {
53+ global $CONF;
54+ $this->templateDir = $templateDir;
55+ $this->lang = ereg_replace( '[\\|/]', '', getLanguageName());
56+ }
57+
58+ function fetch($name, $dir = null, $suffix = 'html') {
59+ $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->lang.( $suffix ? '.'.strtolower($suffix) : '' );
60+ if ( ! file_exists($path) ){
61+ $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->defaultLang.( $suffix ? '.'.strtolower($suffix) : '' );
62+ if ( ! file_exists($path) )
63+ return '';
64+ }
65+
66+ $fsize = filesize($path);
67+ if ($fsize <= 0) return '';
68+
69+ $fd = fopen($path, 'r');
70+ $contents = fread($fd, $fsize);
71+ fclose($fd);
72+ return $contents;
73+ }
74+
75+ function fill($template, $values, $default = null) {
76+ if( $default )
77+ return preg_replace($this->defalutPattern, 'isset($values[\'$1\']) ? (\'$2\' ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']) : $default', $template);
78+ if( $default === null )
79+ return preg_replace($this->defalutPattern, '(\'$2\') ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']', $template);
80+ return preg_replace($this->defalutPattern, 'isset($values[\'$1\']) ? (\'$2\' ? htmlspecialchars($values[\'$1\'], ENT_QUOTES) : $values[\'$1\']) : \'{{$1}}\' ', $template);
81+ }
82+
83+ function fetchAndFill($name, $values, $dir = null, $suffix = 'html', $default = null){
84+ $tpl = $this->fetch($name, $dir, $suffix);
85+ return $this->fill($tpl, $values, $default);
86+ }
87+}
--- a/trunk/NP_Moblog/sharedlibs/sharedlibs.php
+++ b/trunk/NP_Moblog/sharedlibs/sharedlibs.php
@@ -1,51 +1,51 @@
1-<?php
2-// vim: tabstop=2:shiftwidth=2
3-
4-/**
5- * sharedlibs.php ($Revision: 1.1 $)
6- *
7- * by hsur ( http://blog.cles.jp/np_cles )
8- * $Id: sharedlibs.php,v 1.1 2008-05-04 07:04:50 hsur Exp $
9-*/
10-
11-/*
12- * Copyright (C) 2006 CLES. All rights reserved.
13- *
14- * This program is free software; you can redistribute it and/or
15- * modify it under the terms of the GNU General Public License
16- * as published by the Free Software Foundation; either version 2
17- * of the License, or (at your option) any later version.
18- *
19- * This program is distributed in the hope that it will be useful,
20- * but WITHOUT ANY WARRANTY; without even the implied warranty of
21- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22- * GNU General Public License for more details.
23- *
24- * You should have received a copy of the GNU General Public License
25- * along with this program; if not, write to the Free Software
26- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27- *
28- * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29- * permission to link the code of this program with those files in the PEAR
30- * library that are licensed under the PHP License (or with modified versions
31- * of those files that use the same license as those files), and distribute
32- * linked combinations including the two. You must obey the GNU General Public
33- * License in all respects for all of the code used other than those files in
34- * the PEAR library that are licensed under the PHP License. If you modify
35- * this file, you may extend this exception to your version of the file,
36- * but you are not obligated to do so. If you do not wish to do so, delete
37- * this exception statement from your version.
38-*/
39-
40-if (!defined('NP_SHAREDLIBS_LOADED')) {
41- if (!defined('PATH_SEPARATOR')) {
42- if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
43- define('PATH_SEPARATOR', ';');
44- } else {
45- define('PATH_SEPARATOR', ':');
46- }
47- }
48- ini_set('include_path', dirname(__FILE__).PATH_SEPARATOR.ini_get('include_path'));
49-
50- define('NP_SHAREDLIBS_LOADED', true);
51-}
1+<?php
2+// vim: tabstop=2:shiftwidth=2
3+
4+/**
5+ * sharedlibs.php ($Revision: 1.8 $)
6+ *
7+ * by hsur ( http://blog.cles.jp/np_cles )
8+ * $Id: sharedlibs.php,v 1.8 2006/12/12 16:51:05 hsur Exp $
9+*/
10+
11+/*
12+ * Copyright (C) 2006 CLES. All rights reserved.
13+ *
14+ * This program is free software; you can redistribute it and/or
15+ * modify it under the terms of the GNU General Public License
16+ * as published by the Free Software Foundation; either version 2
17+ * of the License, or (at your option) any later version.
18+ *
19+ * This program is distributed in the hope that it will be useful,
20+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+ * GNU General Public License for more details.
23+ *
24+ * You should have received a copy of the GNU General Public License
25+ * along with this program; if not, write to the Free Software
26+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27+ *
28+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
29+ * permission to link the code of this program with those files in the PEAR
30+ * library that are licensed under the PHP License (or with modified versions
31+ * of those files that use the same license as those files), and distribute
32+ * linked combinations including the two. You must obey the GNU General Public
33+ * License in all respects for all of the code used other than those files in
34+ * the PEAR library that are licensed under the PHP License. If you modify
35+ * this file, you may extend this exception to your version of the file,
36+ * but you are not obligated to do so. If you do not wish to do so, delete
37+ * this exception statement from your version.
38+*/
39+
40+if (!defined('NP_SHAREDLIBS_LOADED')) {
41+ if (!defined('PATH_SEPARATOR')) {
42+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
43+ define('PATH_SEPARATOR', ';');
44+ } else {
45+ define('PATH_SEPARATOR', ':');
46+ }
47+ }
48+ ini_set('include_path', dirname(__FILE__).PATH_SEPARATOR.ini_get('include_path'));
49+
50+ define('NP_SHAREDLIBS_LOADED', true);
51+}
Show on old repository browser