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 = "7.0.1.3 (2018/11/12)" ;
080        private static final long serialVersionUID = 701320181112L ;
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         * @og.rev 7.0.1.3 (2018/11/12) useStop 属性は、useTagが有効の場合のみ判定する。
149         *
150         * @return      後続処理の指示
151         */
152        @Override
153        public int doEndTag() {
154                if( usePrintOut ) {
155                        System.out.println( "TextTag=" + value );               // 6.9.3.0 (2018/03/26)
156                }
157                else {
158                        debugPrint();                           // 4.0.0 (2005/02/28)
159                        if( useTag() ) {                        // 6.3.1.0 (2015/06/28)
160                                jspPrint( value );
161                        }
162                        else {
163                                return EVAL_PAGE ;              // 7.0.1.3 (2018/11/12) タグが使用されない場合は、useStop の判定は行わない。
164                        }
165                }
166//              return EVAL_PAGE ;
167                return useStop ? SKIP_PAGE : EVAL_PAGE ; 
168        }
169
170        /**
171         * タグリブオブジェクトをリリースします。
172         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
173         *
174         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
175         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
176         * @og.rev 5.9.10.5 (2016/07/22) xssCheck追加
177         * @og.rev 6.9.3.0 (2018/03/26) usePrintOut 新規追加
178         * @og.rev 6.9.9.1 (2018/08/27) useStop 属性を追加します。
179         *
180         */
181        @Override
182        protected void release2() {
183                super.release2();
184                value           = null;
185                useInclude      = false;
186                usePrintOut     = false;        // 6.9.3.0 (2018/03/26)
187                xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );        // 5.9.10.5 (2016/07/22)
188                useStop         = false;        // 6.9.9.1 (2018/08/27)
189        }
190
191        /**
192         * 【TAG】value 値に設定します。
193         *
194         * @og.tag
195         * ここで、value に設定した場合は、BODY 部は無視されます。
196         * なお、このタグでは、エラー発生時でも継続して処理を続けられるようにします。
197         * error.jsp などのエラー処理画面で、このタグを使用するケースがある為です。
198         *
199         *  <og:text value="あいうえお" />
200         *
201         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
202         * @og.rev 3.0.0.0 (2002/12/25) StringUtil#changeString 廃止
203         * @og.rev 4.0.0.0 (2005/12/31) エラー発生時でも異常終了させずに処理を続けます。
204         *
205         * @param   val 設定値
206         */
207        public void setValue( final String val ) {
208                if( !useInclude ) {
209                        try {
210                                value = getRequestParameter( val );
211                        }
212                        catch( final HybsSystemException ex ) {
213                                value = val ;
214                                LogWriter.log( "val=" + val + " [" + ex.getMessage() + "]" );
215                        }
216                }
217        }
218
219        /**
220         * 【TAG】動的にファイルを include します。
221         *
222         * @og.tag
223         * 指定のファイル名は、自身のディレクトリからの相対パスで表されます。
224         *
225         * @og.rev 4.0.0.0 (2007/05/25) 新規追加
226         * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
227         * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。
228         *
229         * @param   file ファイル名
230         */
231        public void setInclude( final String file ) {
232                useInclude = true;
233
234                final String relativePath = getRequestParameter( file );
235                final String resourcePath = getContextRelativePath(getRequest(), relativePath);
236                final String realPath = HybsSystem.url2dir( resourcePath.substring(1) );
237
238                // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更
239                value = FileUtil.getValue( realPath , HybsConst.UTF_8 );                        // 6.4.5.2 (2016/05/06)
240        }
241
242        /**
243         * 【TAG】デバッグ用に、value 値を System.out.println に 出力するかどうか[true/false]を設定します。
244         *
245         * @og.tag
246         * DOS窓に出力するので、サービス化していない場合のデバッグ用の機能です。
247         * Tomcat内のJSPで、繰返し処理を行っているような場合、処理時間が長い場合、HTMLとしての
248         * レスポンスは、すべての処理が完了するまで戻ってこないため、進捗状況がわかりません。
249         * そこで、DOS窓上に、System.out.printlnとして、value値を出力します。
250         * このフラグを true にセットした場合には、debugも、useTag(タグの使用)も効果は無く、
251         * value値も JSPには表示しません。
252         * 初期値は、(false:出力しない) です。
253         *
254         * @og.rev 6.9.3.0 (2018/03/26) usePrintOut 新規追加
255         *
256         * @param       flag    System.out.println に 出力するかどうか [true:する/false:しない]
257         */
258        public void setUsePrintOut( final String flag ) {
259                usePrintOut = nval( getRequestParameter( flag ),usePrintOut );
260        }
261
262        /**
263         * 【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します
264         *              (初期値:USE_XSS_CHECK[={@og.value org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK}])。
265         *
266         * @og.tag
267         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
268         * (><) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
269         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK}])。
270         *
271         * @og.rev 5.9.10.5 (2016/07/22) xssCheck
272         *
273         * @param       flag    XSSチェック [true:する/false:しない]
274         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
275         */
276        public void setXssCheck( final String flag ) {
277                xssCheck = nval( getRequestParameter( flag ),xssCheck );
278        }
279
280        /**
281         * 【TAG】BODYを処理後に停止するかどうか[true/false]を指定します(初期値:false)。
282         *
283         * @og.tag
284         * true を指定すると、以下の処理は、行われません。
285         * 初期値は、停止しない ("false")
286         *
287         * @og.rev 6.9.9.1 (2018/08/27) useStop 属性を追加します。
288         *
289         * @param   flag 処理後停止 [true:する/それ以外:しない]
290         */
291        public void setUseStop( final String flag ) {
292                useStop = nval( getRequestParameter( flag ),useStop );
293        }
294
295        /**
296         * このオブジェクトの文字列表現を返します。
297         * 基本的にデバッグ目的に使用します。
298         *
299         * @og.rev 6.9.0.2 (2018/02/13) useTag 属性の値も表示に加えます。
300         *
301         * @return このクラスの文字列表現
302         * @og.rtnNotNull
303         */
304        @Override
305        public String toString() {
306                return ToString.title( this.getClass().getName() )
307                                .println( "VERSION"             ,VERSION        )
308                                .println( "useTag"              ,useTag()       )                                       // 6.9.0.2 (2018/02/13)
309                                .println( "value"               ,value          )
310                                .println( "Other..."    ,getAttributes().getAttribute() )
311                                .fixForm().toString() ;
312        }
313
314        /**
315         * 動的にファイルを include する為の、コンテキストパスを求めます。
316         *
317         * 指定のファイル名は、自身のディレクトリからの相対パスで表されます。
318         *
319         * @og.rev 4.0.0.0 (2007/05/25) 新規追加
320         * @og.rev 4.0.0.0 (2007/11/30) if の評価方法を変更します。
321         *
322         * @param       request                 ServletRequestオブジェクト
323         * @param       relativePath    ファイル名
324         *
325         * @return      コンテキストパス
326         */
327        private String getContextRelativePath( final ServletRequest request,final String relativePath ) {
328                // 6.1.0.0 (2014/12/26) refactoring
329                if( StringUtil.startsChar( relativePath , '/' )                         // 6.2.0.0 (2015/02/27) 1文字 String.startsWith
330                        || !(request instanceof HttpServletRequest) ) {
331                                return relativePath ;
332                }
333
334                final HttpServletRequest hrequest = (HttpServletRequest) request;
335                String uri = (String)request.getAttribute("javax.servlet.include.servlet_path");
336                if( uri != null && uri.lastIndexOf('/') >= 0 ) {
337                        final String pathInfo = (String)request.getAttribute("javax.servlet.include.path_info");
338                        if( pathInfo == null ) {
339                                uri = uri.substring(0, uri.lastIndexOf('/'));
340                        }
341                }
342                else {
343                        uri = hrequest.getServletPath();
344                        if( uri.lastIndexOf('/') >= 0 ) {
345                                uri = uri.substring(0, uri.lastIndexOf('/'));
346                        }
347                }
348                return uri + '/' + relativePath;
349        }
350}