001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.taglib;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.fukurou.system.LogWriter;
021import org.opengion.fukurou.system.HybsConst;                                   // 6.4.5.2 (2016/05/06)
022import org.opengion.fukurou.util.FileUtil;                                              // 6.4.5.2 (2016/05/06)
023import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
024import org.opengion.fukurou.util.StringUtil ;                                   // 6.2.0.0 (2015/02/27)
025
026import static org.opengion.fukurou.util.StringUtil.nval;                // 5.9.10.5 (2016/07/22)
027
028import javax.servlet.ServletRequest ;
029import javax.servlet.http.HttpServletRequest ;
030
031/**
032 * BODY部に記述されたエンジン固有の文字列({@XXXX}など)を、
033 * ユーザー情報のエンコーディングに変換するタグです。
034 *
035 * XML形式で 日本語コードのパースが、JSPエンジン(Tomcat)でサポート
036 * されるまでの、暫定的なタグです。
037 * なお、このタグの内部に存在するカスタムタグは、先に実行されるため
038 * 漢字コードは、変換されません。
039 *
040 * ※ 6.3.1.0 (2015/06/28) caseKey、caseVal、caseNN、caseNull、caseIf 属性追加
041 *
042 * @og.formSample
043 * ●形式:<og:text >・・・</og:text>
044 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
045 *
046 * ●Tag定義:
047 *   <og:text
048 *       value              【TAG】value 値に直接書かれたコードを出力します
049 *       include            【TAG】動的にファイルを include します(初期値:null)
050 *       usePrintOut        【TAG】デバッグ用に、value 値を System.out.println に 出力するかどうか(初期値:false) 6.9.3.0 (2018/03/26)
051 *       useStop            【TAG】6.9.9.1 (2018/08/27) BODYを処理後に停止するかどうか[true/false]を指定します(初期値:false)
052 *       xssCheck           【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true])
053 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
054 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
055 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
056 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
057 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
058 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
059 *   >   ... Body ...
060 *   </og:text>
061 *
062 * ●使用例
063 *      ・<og:text >
064 *            <p>あいおえお:<input name="PN" value="{@PN}" /> </p>
065 *        </og:text>
066 *      ・<og:text value="あいうえお" />
067 *
068 *     動的にファイルを include することが出来ます。
069 *      ・<og:text include="{@query}.txt" />
070 *
071 * @og.group 画面部品
072 *
073 * @version  4.0
074 * @author   Kazuhiko Hasegawa
075 * @since    JDK5.0,
076 */
077public class TextTag extends CommonTagSupport {
078        /** このプログラムのVERSION文字列を設定します。   {@value} */
079        private static final String VERSION = "6.9.9.1 (2018/08/27)" ;
080        private static final long serialVersionUID = 699120180827L ;
081
082        private String  value           ;
083        private boolean useInclude      ;
084        private boolean usePrintOut     ;       // 6.9.3.0 (2018/03/26)
085
086        private boolean xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );        // 5.9.10.5 (2016/07/22) XSS対策
087
088        // 6.9.9.1 (2018/08/27) BODYを処理後に停止するかどうかを指定します。
089        private boolean useStop         ;       // BODYを処理後に停止(true)するかどうか
090
091        /**
092         * デフォルトコンストラクター
093         *
094         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
095         */
096        public TextTag() { super(); }           // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
097
098        /**
099         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
100         *
101         * @og.rev 6.3.1.0 (2015/06/28) caseKey、caseVal、caseNN、caseNull、caseIf 属性追加
102         *
103         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
104         */
105        @Override
106        public int doStartTag() {
107                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
108                return useTag() && !useInclude && value == null
109                                        ? EVAL_BODY_BUFFERED            // Body を評価する。( extends BodyTagSupport 時)
110                                        : SKIP_BODY ;                           // Body を評価しない
111        }
112
113        /**
114         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
115         *
116         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
117         * @og.rev 3.0.0.0 (2002/12/25) StringUtil#changeString 廃止
118         * @og.rev 3.1.1.0 (2003/03/28) ボディの内容を取得する処理を、CommonTagSupport で行う。
119         * @og.rev 4.0.0.0 (2007/10/12) 処理中にエラーを発生させないようにしする。
120         * @og.rev 6.3.1.0 (2015/06/28) caseKey、caseVal、caseNN、caseNull、caseIf 属性追加
121         * @og.rev 5.9.10.5 (2016/07/22) XSS対策
122         *
123         * @return      後続処理の指示(SKIP_BODY)
124         */
125        @Override
126        public int doAfterBody() {
127                // 6.3.1.0 (2015/06/28) caseKey、caseVal、caseNN、caseNull、caseIf 属性追加
128                // 4.0.0.0 (2007/10/12) 処理中にエラーを発生させない
129                try {
130                        useXssCheck( xssCheck );                // 5.9.10.5 (2016/07/22)
131                        value = getBodyString();
132                }
133                catch( final HybsSystemException ex ) { // 主に、UserInfo が見つからない場合
134                        value = getBodyContent().getString() ;
135                }
136
137                return SKIP_BODY ;
138        }
139
140        /**
141         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
142         *
143         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
144         * @og.rev 6.3.1.0 (2015/06/28) caseKey、caseVal、caseNN、caseNull、caseIf 属性追加
145         * @og.rev 6.9.0.2 (2018/02/13) debug="true" で、System.out.println( value ) を出します。
146         * @og.rev 6.9.3.0 (2018/03/26) debug="true" ではなく、専用属性の usePrintOut で出力を制御します。
147         * @og.rev 6.9.9.1 (2018/08/27) useStop 属性を追加します。
148         *
149         * @return      後続処理の指示
150         */
151        @Override
152        public int doEndTag() {
153                if( usePrintOut ) {
154                        System.out.println( "TextTag=" + value );               // 6.9.3.0 (2018/03/26)
155                }
156                else {
157                        debugPrint();                   // 4.0.0 (2005/02/28)
158                        if( useTag() ) {                // 6.3.1.0 (2015/06/28)
159                                jspPrint( value );
160                        }
161                }
162//              return EVAL_PAGE ;
163                return useStop ? SKIP_PAGE : EVAL_PAGE ; 
164        }
165
166        /**
167         * タグリブオブジェクトをリリースします。
168         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
169         *
170         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
171         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
172         * @og.rev 5.9.10.5 (2016/07/22) xssCheck追加
173         * @og.rev 6.9.3.0 (2018/03/26) usePrintOut 新規追加
174         * @og.rev 6.9.9.1 (2018/08/27) useStop 属性を追加します。
175         *
176         */
177        @Override
178        protected void release2() {
179                super.release2();
180                value           = null;
181                useInclude      = false;
182                usePrintOut     = false;        // 6.9.3.0 (2018/03/26)
183                xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );        // 5.9.10.5 (2016/07/22)
184                useStop         = false;        // 6.9.9.1 (2018/08/27)
185        }
186
187        /**
188         * 【TAG】value 値に設定します。
189         *
190         * @og.tag
191         * ここで、value に設定した場合は、BODY 部は無視されます。
192         * なお、このタグでは、エラー発生時でも継続して処理を続けられるようにします。
193         * error.jsp などのエラー処理画面で、このタグを使用するケースがある為です。
194         *
195         *  <og:text value="あいうえお" />
196         *
197         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
198         * @og.rev 3.0.0.0 (2002/12/25) StringUtil#changeString 廃止
199         * @og.rev 4.0.0.0 (2005/12/31) エラー発生時でも異常終了させずに処理を続けます。
200         *
201         * @param   val 設定値
202         */
203        public void setValue( final String val ) {
204                if( !useInclude ) {
205                        try {
206                                value = getRequestParameter( val );
207                        }
208                        catch( final HybsSystemException ex ) {
209                                value = val ;
210                                LogWriter.log( "val=" + val + " [" + ex.getMessage() + "]" );
211                        }
212                }
213        }
214
215        /**
216         * 【TAG】動的にファイルを include します。
217         *
218         * @og.tag
219         * 指定のファイル名は、自身のディレクトリからの相対パスで表されます。
220         *
221         * @og.rev 4.0.0.0 (2007/05/25) 新規追加
222         * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
223         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
224         *
225         * @param   file ファイル名
226         */
227        public void setInclude( final String file ) {
228                useInclude = true;
229
230                final String relativePath = getRequestParameter( file );
231                final String resourcePath = getContextRelativePath(getRequest(), relativePath);
232                final String realPath = HybsSystem.url2dir( resourcePath.substring(1) );
233
234                // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
235                value = FileUtil.getValue( realPath , HybsConst.UTF_8 );                        // 6.4.5.2 (2016/05/06)
236        }
237
238        /**
239         * 【TAG】デバッグ用に、value 値を System.out.println に 出力するかどうか[true/false]を設定します。
240         *
241         * @og.tag
242         * DOS窓に出力するので、サービス化していない場合のデバッグ用の機能です。
243         * Tomcat内のJSPで、繰返し処理を行っているような場合、処理時間が長い場合、HTMLとしての
244         * レスポンスは、すべての処理が完了するまで戻ってこないため、進捗状況がわかりません。
245         * そこで、DOS窓上に、System.out.printlnとして、value値を出力します。
246         * このフラグを true にセットした場合には、debugも、useTag(タグの使用)も効果は無く、
247         * value値も JSPには表示しません。
248         * 初期値は、(false:出力しない) です。
249         *
250         * @og.rev 6.9.3.0 (2018/03/26) usePrintOut 新規追加
251         *
252         * @param       flag    System.out.println に 出力するかどうか [true:する/false:しない]
253         */
254        public void setUsePrintOut( final String flag ) {
255                usePrintOut = nval( getRequestParameter( flag ),usePrintOut );
256        }
257
258        /**
259         * 【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します
260         *              (初期値:USE_XSS_CHECK[={@og.value org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK}])。
261         *
262         * @og.tag
263         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
264         * (><) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
265         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK}])。
266         *
267         * @og.rev 5.9.10.5 (2016/07/22) xssCheck
268         *
269         * @param       flag    XSSチェック [true:する/false:しない]
270         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
271         */
272        public void setXssCheck( final String flag ) {
273                xssCheck = nval( getRequestParameter( flag ),xssCheck );
274        }
275
276        /**
277         * 【TAG】BODYを処理後に停止するかどうか[true/false]を指定します(初期値:false)。
278         *
279         * @og.tag
280         * true を指定すると、以下の処理は、行われません。
281         * 初期値は、停止しない ("false")
282         *
283         * @og.rev 6.9.9.1 (2018/08/27) useStop 属性を追加します。
284         *
285         * @param   flag 処理後停止 [true:する/それ以外:しない]
286         */
287        public void setUseStop( final String flag ) {
288                useStop = nval( getRequestParameter( flag ),useStop );
289        }
290
291        /**
292         * このオブジェクトの文字列表現を返します。
293         * 基本的にデバッグ目的に使用します。
294         *
295         * @og.rev 6.9.0.2 (2018/02/13) useTag 属性の値も表示に加えます。
296         *
297         * @return このクラスの文字列表現
298         * @og.rtnNotNull
299         */
300        @Override
301        public String toString() {
302                return ToString.title( this.getClass().getName() )
303                                .println( "VERSION"             ,VERSION        )
304                                .println( "useTag"              ,useTag()       )                                       // 6.9.0.2 (2018/02/13)
305                                .println( "value"               ,value          )
306                                .println( "Other..."    ,getAttributes().getAttribute() )
307                                .fixForm().toString() ;
308        }
309
310        /**
311         * 動的にファイルを include する為の、コンテキストパスを求めます。
312         *
313         * 指定のファイル名は、自身のディレクトリからの相対パスで表されます。
314         *
315         * @og.rev 4.0.0.0 (2007/05/25) 新規追加
316         * @og.rev 4.0.0.0 (2007/11/30) if の評価方法を変更します。
317         *
318         * @param       request                 ServletRequestオブジェクト
319         * @param       relativePath    ファイル名
320         *
321         * @return      コンテキストパス
322         */
323        private String getContextRelativePath( final ServletRequest request,final String relativePath ) {
324                // 6.1.0.0 (2014/12/26) refactoring
325                if( StringUtil.startsChar( relativePath , '/' )                         // 6.2.0.0 (2015/02/27) 1文字 String.startsWith
326                        || !(request instanceof HttpServletRequest) ) {
327                                return relativePath ;
328                }
329
330                final HttpServletRequest hrequest = (HttpServletRequest) request;
331                String uri = (String)request.getAttribute("javax.servlet.include.servlet_path");
332                if( uri != null && uri.lastIndexOf('/') >= 0 ) {
333                        final String pathInfo = (String)request.getAttribute("javax.servlet.include.path_info");
334                        if( pathInfo == null ) {
335                                uri = uri.substring(0, uri.lastIndexOf('/'));
336                        }
337                }
338                else {
339                        uri = hrequest.getServletPath();
340                        if( uri.lastIndexOf('/') >= 0 ) {
341                                uri = uri.substring(0, uri.lastIndexOf('/'));
342                        }
343                }
344                return uri + '/' + relativePath;
345        }
346}