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