    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.plugin;
    020
    021import org.apache.wiki.api.core.Attachment;
    022import org.apache.wiki.api.core.Context;
    023import org.apache.wiki.api.core.ContextEnum;
    024import org.apache.wiki.api.core.Engine;
    025import org.apache.wiki.api.exceptions.PluginException;
    026import org.apache.wiki.api.exceptions.ProviderException;
    027import org.apache.wiki.api.plugin.Plugin;
    028import org.apache.wiki.attachment.AttachmentManager;
    029import org.apache.wiki.parser.MarkupParser;
    030import org.apache.wiki.util.TextUtil;
    031
    032import java.util.Map;
    033
    034
    035/**
    036 *  Provides an image plugin for better control than is possible with a simple image inclusion.
    037 *  <br> Most parameters are equivalents of the html image attributes.
    038 *
    039 *  <p>Parameters : </p>
    040 *  <ul>
    041 *  <li><b>src</b> - the source (a URL) of the image (required parameter)</li>
    042 *  <li><b>align</b> - the alignment of the image</li>
    043 *  <li><b>height</b> - the height of the image</li>
    044 *  <li><b>width</b> - the width of the image</li>
    045 *  <li><b>alt</b> - alternate text</li>
    046 *  <li><b>caption</b> - the caption for the image</li>
    047 *  <li><b>link</b> - the hyperlink for the image</li>
    048 *  <li><b>target</b> - the target (frame) to be used for opening the image</li>
    049 *  <li><b>style</b> - the style attribute of the image</li>
    050 *  <li><b>class</b> - the associated class for the image</li>
    051 *  <li><b>border</b> - the border for the image</li>
    052 *  <li><b>title</b> - the title for the image, can be presented as a tooltip to the user</li>
    053 *  </ul>
    054 *
    055 *  @since 2.1.4.
    056 */
    057// FIXME: It is not yet possible to do wiki internal links.  In order to do this cleanly, a TranslatorReader revamp is needed.
    058public class Image implements Plugin {
    059
    060    /** The parameter name for setting the src.  Value is <tt>{@value}</tt>. */
    061    public static final String PARAM_SRC      = "src";
    062    /** The parameter name for setting the align parameter.  Value is <tt>{@value}</tt>. */
    063    public static final String PARAM_ALIGN    = "align";
    064    /** The parameter name for setting the height.  Value is <tt>{@value}</tt>. */
    065    public static final String PARAM_HEIGHT   = "height";
    066    /** The parameter name for setting the width.  Value is <tt>{@value}</tt>. */
    067    public static final String PARAM_WIDTH    = "width";
    068    /** The parameter name for setting the alt.  Value is <tt>{@value}</tt>. */
    069    public static final String PARAM_ALT      = "alt";
    070    /** The parameter name for setting the caption.  Value is <tt>{@value}</tt>. */
    071    public static final String PARAM_CAPTION  = "caption";
    072    /** The parameter name for setting the link.  Value is <tt>{@value}</tt>. */
    073    public static final String PARAM_LINK     = "link";
    074    /** The parameter name for setting the target.  Value is <tt>{@value}</tt>. */
    075    public static final String PARAM_TARGET   = "target";
    076    /** The parameter name for setting the style.  Value is <tt>{@value}</tt>. */
    077    public static final String PARAM_STYLE    = "style";
    078    /** The parameter name for setting the class.  Value is <tt>{@value}</tt>. */
    079    public static final String PARAM_CLASS    = "class";
    080    /** The parameter name for setting the border.  Value is <tt>{@value}</tt>. */
    081    public static final String PARAM_BORDER   = "border";
    082    /** The parameter name for setting the title.  Value is <tt>{@value}</tt>. */
    083    public static final String PARAM_TITLE    = "title";
    084
    085    /**
    086     *  This method is used to clean away things like quotation marks which
    087     *  a malicious user could use to stop processing and insert javascript.
    088     */
    089    private static String getCleanParameter( final Map< String, String > params, final String paramId ) {
    090        return TextUtil.replaceEntities( params.get( paramId ) );
    091    }
    092    private boolean needsSanitization(String link) {
    093        String testVal = link.toLowerCase().replaceAll("\\s+", "").trim();
    094        if (testVal.startsWith("data")
    095                || testVal.startsWith("javascript")
    096                || testVal.startsWith("vbscript")) {
    097            return true;
    098        }
    099        return false;
    100    }
    101    /**
    102     *  {@inheritDoc}
    103     */
    104    @Override
    105    public String execute( final Context context, final Map<String, String> params ) throws PluginException {
    106        final Engine engine  = context.getEngine();
    107        String src           = getCleanParameter( params, PARAM_SRC );
    108        final String align   = getCleanParameter( params, PARAM_ALIGN );
    109        final String ht      = getCleanParameter( params, PARAM_HEIGHT );
    110        final String wt      = getCleanParameter( params, PARAM_WIDTH );
    111        final String alt     = getCleanParameter( params, PARAM_ALT );
    112        final String caption = getCleanParameter( params, PARAM_CAPTION );
    113        String link          = getCleanParameter( params, PARAM_LINK );
    114        String target        = getCleanParameter( params, PARAM_TARGET );
    115        final String style   = getCleanParameter( params, PARAM_STYLE );
    116        final String cssclass= getCleanParameter( params, PARAM_CLASS );
    117        final String border  = getCleanParameter( params, PARAM_BORDER );
    118        final String title   = getCleanParameter( params, PARAM_TITLE );
    119
    120        if( src == null ) {
    121            throw new PluginException("Parameter 'src' is required for Image plugin");
    122        }
    123
    124        //if( cssclass == null ) cssclass = "imageplugin";
    125
    126        if( target != null && !validTargetValue(target) ) {
    127            target = null; // not a valid value so ignore
    128        }
    129
    130        try {
    131            final AttachmentManager mgr = engine.getManager( AttachmentManager.class );
    132            final Attachment att = mgr.getAttachmentInfo( context, src );
    133
    134            if( att != null ) {
    135                src = context.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), att.getName() );
    136            }
    137        } catch( final ProviderException e ) {
    138            throw new PluginException( "Attachment info failed: " + e.getMessage() );
    139        }
    140
    141        final StringBuilder result = new StringBuilder();
    142
    143        result.append( "<table border=\"0\" class=\"imageplugin\"" );
    144
    145        if( title != null ) {
    146            result.append( " title=\"" ).append( title ).append( "\"" );
    147        }
    148
    149        if( align != null ) {
    150            if( align.equals( "center" ) ) {
    151                result.append( " style=\"margin-left: auto; margin-right: auto; text-align:center; vertical-align:middle;\"" );
    152            } else {
    153                result.append( " style=\"float:" ).append( align ).append( ";\"" );
    154            }
    155        }
    156
    157        result.append( ">\n" );
    158
    159        if( caption != null ) {
    160            result.append( "<caption>" ).append( caption ).append( "</caption>\n" );
    161        }
    162
    163        // move css class and style to the container of the image, so it doesn't affect the caption
    164        result.append( "<tr><td" );
    165
    166        if( cssclass != null ) {
    167            result.append( " class=\"" ).append( cssclass ).append( "\"" );
    168        }
    169
    170        if( style != null ) {
    171            result.append( " style=\"" ).append( style );
    172
    173            // Make sure that we add a ";" to the end of the style string
    174            if( result.charAt( result.length()-1 ) != ';' ) {
    175                result.append( ";" );
    176            }
    177
    178            result.append("\"");
    179        }
    180
    181        result.append( ">" );
    182
    183        if( link != null ) {
    184            if( !context.getBooleanWikiProperty( MarkupParser.PROP_ALLOWHTML, false ) ) {
    185                if (needsSanitization(link)) {
    186                    link = "http://invalid_url" + link;
    187                }
    188            }
    189            result.append( "<a href=\"" ).append( link ).append( "\"" );
    190            if( target != null ) {
    191                result.append( " target=\"" ).append( target ).append( "\"" );
    192            }
    193            result.append(">");
    194        }
    195
    196        if(!context.getBooleanWikiProperty(MarkupParser.PROP_ALLOWHTML, false)) {
    197            if (needsSanitization(src)) {
    198                src = "http://invalid_url" + src;
    199            }
    200        }
    201        result.append( "<img src=\"" ).append( src ).append( "\"" );
    202
    203        if( ht != null ) {
    204            result.append( " height=\"" ).append( ht ).append( "\"" );
    205        }
    206        if( wt != null ) {
    207            result.append( " width=\"" ).append( wt ).append( "\"" );
    208        }
    209        if( alt != null ) {
    210            result.append( " alt=\"" ).append( alt ).append( "\"" );
    211        }
    212        if( border != null ) {
    213            result.append( " border=\"" ).append( border ).append( "\"" );
    214        }
    215        // if( map != null )    result.append(" map=\""+map+"\"");
    216
    217        result.append(" />");
    218        if( link != null ) {
    219            result.append("</a>");
    220        }
    221        result.append("</td></tr>\n");
    222        result.append("</table>\n");
    223
    224        return result.toString();
    225    }
    226
    227    private boolean validTargetValue( final String s ) {
    228        if( s.equals("_blank")
    229            || s.equals("_self")
    230            || s.equals("_parent")
    231            || s.equals("_top") ) {
    232            return true;
    233        } else if( !s.isEmpty() ) { // check [a-zA-z]
    234            final char c = s.charAt(0);
    235            return Character.isLowerCase(c) || Character.isUpperCase(c);
    236        }
    237        return false;
    238    }
    239
    240}