    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.xmlrpc;
    020
    021import org.apache.wiki.LinkCollector;
    022import org.apache.wiki.api.core.Attachment;
    023import org.apache.wiki.api.core.Context;
    024import org.apache.wiki.api.core.ContextEnum;
    025import org.apache.wiki.api.core.Page;
    026import org.apache.wiki.api.spi.Wiki;
    027import org.apache.wiki.auth.permissions.PagePermission;
    028import org.apache.wiki.auth.permissions.PermissionFactory;
    029import org.apache.wiki.pages.PageManager;
    030import org.apache.wiki.render.RenderingManager;
    031import org.apache.xmlrpc.XmlRpcException;
    032
    033import java.util.Calendar;
    034import java.util.Date;
    035import java.util.Hashtable;
    036import java.util.Set;
    037import java.util.Vector;
    038import java.util.stream.Collectors;
    039
    040/**
    041 *  Provides handlers for all RPC routines.  These routines are used by
    042 *  the UTF-8 interface.
    043 *
    044 *  @since 1.6.13
    045 */
    046
    047public class RPCHandlerUTF8 extends AbstractRPCHandler {
    048
    049    public String getApplicationName() {
    050        checkPermission( PagePermission.VIEW );
    051        return m_engine.getApplicationName();
    052    }
    053
    054    public Vector< String > getAllPages() {
    055        checkPermission( PagePermission.VIEW );
    056
    057        final Set< Page > pages = m_engine.getManager( PageManager.class ).getRecentChanges();
    058
    059        return pages.stream().filter(p -> !(p instanceof Attachment)).map(Page::getName).collect(Collectors.toCollection(Vector::new));
    060    }
    061
    062    /**
    063     *  Encodes a single wiki page info into a Hashtable.
    064     */
    065    @Override
    066    protected Hashtable<String, Object> encodeWikiPage( final Page page ) {
    067        final Hashtable<String, Object> ht = new Hashtable<>();
    068        ht.put( "name", page.getName() );
    069
    070        final Date d = page.getLastModified();
    071
    072        //
    073        //  Here we reset the DST and TIMEZONE offsets of the calendar.  Unfortunately, I haven't thought of a better
    074        //  way to ensure that we're getting the proper date from the XML-RPC thingy, except to manually adjust the date.
    075        //
    076        final Calendar cal = Calendar.getInstance();
    077        cal.setTime( d );
    078        cal.add( Calendar.MILLISECOND,
    079                 - (cal.get( Calendar.ZONE_OFFSET ) +
    080                    (cal.getTimeZone().inDaylightTime( d ) ? cal.get( Calendar.DST_OFFSET ) : 0 )) );
    081
    082        ht.put( "lastModified", cal.getTime() );
    083        ht.put( "version", page.getVersion() );
    084
    085        if( page.getAuthor() != null ) {
    086            ht.put( "author", page.getAuthor() );
    087        }
    088
    089        return ht;
    090    }
    091
    092    @Override
    093    public Vector< Hashtable< String, Object > > getRecentChanges( Date since ) {
    094        checkPermission( PagePermission.VIEW );
    095
    096        final Set< Page > pages = m_engine.getManager( PageManager.class ).getRecentChanges();
    097        final Vector< Hashtable< String, Object > > result = new Vector<>();
    098
    099        final Calendar cal = Calendar.getInstance();
    100        cal.setTime( since );
    101
    102        //
    103        //  Convert UTC to our time.
    104        //
    105        cal.add( Calendar.MILLISECOND,
    106                 (cal.get( Calendar.ZONE_OFFSET ) +
    107                  (cal.getTimeZone().inDaylightTime(since) ? cal.get( Calendar.DST_OFFSET ) : 0 ) ) );
    108        since = cal.getTime();
    109
    110        for( final Page page : pages ) {
    111            if( page.getLastModified().after( since ) && !( page instanceof Attachment ) ) {
    112                result.add( encodeWikiPage( page ) );
    113            }
    114        }
    115
    116        return result;
    117    }
    118
    119    /**
    120     *  Simple helper method, turns the incoming page name into
    121     *  normal Java string, then checks page condition.
    122     *
    123     *  @param pagename Page Name as an RPC string (URL-encoded UTF-8)
    124     *  @return Real page name, as Java string.
    125     *  @throws XmlRpcException, if there is something wrong with the page.
    126     */
    127    private String parsePageCheckCondition( final String pagename ) throws XmlRpcException {
    128        if( !m_engine.getManager( PageManager.class ).wikiPageExists(pagename) ) {
    129            throw new XmlRpcException( ERR_NOPAGE, "No such page '"+pagename+"' found, o master." );
    130        }
    131
    132        final Page p = m_engine.getManager( PageManager.class ).getPage( pagename );
    133
    134        checkPermission( PermissionFactory.getPagePermission( p, PagePermission.VIEW_ACTION ) );
    135        return pagename;
    136    }
    137
    138    public Hashtable<String, Object> getPageInfo( final String pagename ) throws XmlRpcException {
    139        return encodeWikiPage( m_engine.getManager( PageManager.class ).getPage( parsePageCheckCondition( pagename ) ) );
    140    }
    141
    142    public Hashtable<String, Object> getPageInfoVersion( String pagename, final int version ) throws XmlRpcException {
    143        pagename = parsePageCheckCondition( pagename );
    144
    145        return encodeWikiPage( m_engine.getManager( PageManager.class ).getPage( pagename, version ) );
    146    }
    147
    148    public String getPage( final String pagename ) throws XmlRpcException {
    149        return m_engine.getManager( PageManager.class ).getPureText( parsePageCheckCondition( pagename ), -1 );
    150    }
    151
    152    public String getPageVersion( final String pagename, final int version ) throws XmlRpcException {
    153        return m_engine.getManager( PageManager.class ).getPureText( parsePageCheckCondition( pagename ), version );
    154    }
    155
    156    public String getPageHTML( final String pagename ) throws XmlRpcException  {
    157        return m_engine.getManager( RenderingManager.class ).getHTML( parsePageCheckCondition( pagename ) );
    158    }
    159
    160    public String getPageHTMLVersion( final String pagename, final int version ) throws XmlRpcException {
    161        return m_engine.getManager( RenderingManager.class ).getHTML( parsePageCheckCondition( pagename ), version );
    162    }
    163
    164    public Vector< Hashtable< String, String > > listLinks( String pagename ) throws XmlRpcException {
    165        pagename = parsePageCheckCondition( pagename );
    166
    167        final Page page = m_engine.getManager( PageManager.class ).getPage( pagename );
    168        final String pagedata = m_engine.getManager( PageManager.class ).getPureText( page );
    169
    170        final LinkCollector localCollector = new LinkCollector();
    171        final LinkCollector extCollector   = new LinkCollector();
    172        final LinkCollector attCollector   = new LinkCollector();
    173
    174        final Context context = Wiki.context().create( m_engine, page );
    175        m_engine.getManager( RenderingManager.class ).textToHTML( context, pagedata, localCollector, extCollector, attCollector );
    176
    177        final Vector< Hashtable< String, String > > result = new Vector<>();
    178
    179        // FIXME: Contains far too much common with RPCHandler.  Refactor!
    180
    181        //
    182        //  Add local links.
    183        //
    184        for( final String link : localCollector.getLinks() ) {
    185            final Hashtable<String, String> ht = new Hashtable<>();
    186            ht.put( "page", link );
    187            ht.put( "type", LINK_LOCAL );
    188
    189            if( m_engine.getManager( PageManager.class ).wikiPageExists( link ) ) {
    190                ht.put( "href", context.getViewURL( link ) );
    191            } else {
    192                ht.put( "href", context.getURL( ContextEnum.PAGE_EDIT.getRequestContext(), link ) );
    193            }
    194
    195            result.add( ht );
    196        }
    197
    198        //
    199        // Add links to inline attachments
    200        //
    201        for( final String link : attCollector.getLinks() ) {
    202            final Hashtable<String, String> ht = new Hashtable<>();
    203            ht.put( "page", link );
    204            ht.put( "type", LINK_LOCAL );
    205            ht.put( "href", context.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), link ) );
    206            result.add( ht );
    207        }
    208
    209        //
    210        // External links don't need to be changed into XML-RPC strings, simply because URLs are by definition ASCII.
    211        //
    212        for( final String link : extCollector.getLinks() )  {
    213            final Hashtable<String, String> ht = new Hashtable<>();
    214            ht.put( "page", link );
    215            ht.put( "type", LINK_EXTERNAL );
    216            ht.put( "href", link );
    217            result.add( ht );
    218        }
    219
    220        return result;
    221    }
    222
    223}