    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.rpc.atom;
    020
    021import com.rometools.rome.feed.synd.SyndContent;
    022import com.rometools.rome.feed.synd.SyndContentImpl;
    023import com.rometools.rome.feed.synd.SyndEntry;
    024import com.rometools.rome.feed.synd.SyndEntryImpl;
    025import com.rometools.rome.feed.synd.SyndFeed;
    026import com.rometools.rome.feed.synd.SyndFeedImpl;
    027import com.rometools.rome.feed.synd.SyndLink;
    028import com.rometools.rome.feed.synd.SyndLinkImpl;
    029import com.rometools.rome.io.SyndFeedInput;
    030import com.rometools.rome.io.SyndFeedOutput;
    031import org.apache.logging.log4j.LogManager;
    032import org.apache.logging.log4j.Logger;
    033import org.apache.wiki.api.core.Context;
    034import org.apache.wiki.api.core.Engine;
    035import org.apache.wiki.api.core.Page;
    036import org.apache.wiki.api.exceptions.ProviderException;
    037import org.apache.wiki.api.exceptions.WikiException;
    038import org.apache.wiki.api.spi.Wiki;
    039import org.apache.wiki.pages.PageManager;
    040import org.apache.wiki.plugin.WeblogEntryPlugin;
    041import org.apache.wiki.plugin.WeblogPlugin;
    042import org.apache.wiki.util.TextUtil;
    043
    044import jakarta.servlet.ServletConfig;
    045import jakarta.servlet.ServletException;
    046import jakarta.servlet.http.HttpServlet;
    047import jakarta.servlet.http.HttpServletRequest;
    048import jakarta.servlet.http.HttpServletResponse;
    049import java.io.IOException;
    050import java.io.InputStreamReader;
    051import java.util.ArrayList;
    052import java.util.Collection;
    053import java.util.Date;
    054import java.util.List;
    055import org.apache.wiki.util.HttpUtil;
    056
    057
    058/**
    059 *  Handles incoming requests for the Atom API.  This class uses the "sandler" Atom API implementation.
    060 *
    061 *  @since 2.1.97
    062 */
    063// FIXME: Rewrite using some other library
    064public class AtomAPIServlet extends HttpServlet {
    065
    066    private static final Logger LOG = LogManager.getLogger( AtomAPIServlet.class );
    067
    068    private static final long serialVersionUID = 0L;
    069
    070    private Engine m_engine;
    071
    072    /**
    073     *  {@inheritDoc}
    074     */
    075    @Override
    076    public void init( final ServletConfig config ) throws ServletException {
    077        m_engine = Wiki.engine().find( config );
    078    }
    079
    080    /**
    081     *  Takes the name of the page from the request URI.
    082     *  The initial slash is also removed.  If there is no page,
    083     *  returns null.
    084     */
    085    private String getPageName( final HttpServletRequest request ) {
    086        String name = request.getPathInfo();
    087        if( name == null || name.length() <= 1 ) {
    088            return null;
    089        } else if( name.charAt( 0 ) == '/' ) {
    090            name = name.substring( 1 );
    091        }
    092
    093        return TextUtil.urlDecodeUTF8( name );
    094    }
    095
    096    /**
    097     *  Implements the PostURI of the Atom spec.
    098     *  <p>
    099     *  Implementation notes:
    100     *  <ul>
    101     *   <li>Only fetches the first content.  All other contents are ignored.
    102     *   <li>Assumes that incoming code is plain text or WikiMarkup, not html.
    103     *  </ul>
    104     *
    105     *  {@inheritDoc}
    106     */
    107    @Override
    108    public void doPost( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException {
    109        LOG.debug( "Received POST to AtomAPIServlet" );
    110
    111        try {
    112            final String blogid = getPageName( request );
    113            final Page page = m_engine.getManager( PageManager.class ).getPage( blogid );
    114            if( page == null ) {
    115                throw new ServletException( "Page " + blogid + " does not exist, cannot add blog post." );
    116            }
    117
    118            // FIXME: Do authentication here
    119            SyndFeed entry = new SyndFeedInput().build(new InputStreamReader(request.getInputStream() ));
    120
    121
    122            //  Fetch the obligatory parts of the content.
    123            final String title = entry.getTitle();
    124            final SyndEntry content = entry.getEntries().get( 0 );
    125            final String author = entry.getAuthor();
    126
    127            // FIXME: Sandler 0.5 does not support generator
    128            // Generate new blog entry.
    129            final WeblogEntryPlugin plugin = new WeblogEntryPlugin();
    130            final String pageName = plugin.getNewEntryPage( m_engine, blogid );
    131            final String username = author;
    132            final Page entryPage = Wiki.contents().page( m_engine, pageName );
    133            entryPage.setAuthor( username );
    134
    135            final Context context = Wiki.context().create( m_engine, request, entryPage );
    136            final StringBuilder text = new StringBuilder();
    137            text.append( "!" )
    138                .append( title )
    139                .append( "\n\n" )
    140                .append( content );
    141            LOG.debug( "Writing entry: " + text );
    142            m_engine.getManager( PageManager.class ).saveText( context, text.toString() );
    143        } catch( final IOException e ) {
    144            LOG.error("I/O exception",e);
    145            throw new ServletException("Could not get body of request");
    146        } catch( final WikiException e ) {
    147            LOG.error("Provider exception while posting",e);
    148            throw new ServletException("JSPWiki cannot save the entry");
    149        } catch( final Exception e ) {
    150            LOG.error("Received faulty Atom entry",e);
    151            throw new ServletException("Faulty Atom entry");
    152        }
    153    }
    154
    155    /**
    156     *  Handles HTTP GET.  However, we do not respond to GET requests,
    157     *  other than to show an explanatory text.
    158     *
    159     *  {@inheritDoc}
    160     */
    161    @Override
    162    public void doGet( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException {
    163        LOG.debug( "Received HTTP GET to AtomAPIServlet" );
    164        final String blogid = getPageName( request );
    165        LOG.debug( "Requested page " + blogid );
    166        try {
    167            if( blogid == null ) {
    168                final  SyndFeed feed = listBlogs(request);
    169                response.setContentType( "application/x.atom+xml; charset=UTF-8" );
    170                response.setHeader("Content-Disposition", " attachment; filename=\"atom.xml\"");
    171                feed.setFeedType("atom_0.3");
    172                SyndFeedOutput output = new SyndFeedOutput();
    173                output.output(feed,response.getWriter());
    174            } else {
    175                final SyndEntry entry = getBlogEntry( blogid );
    176                response.setContentType( "application/x.atom+xml; charset=UTF-8" );
    177                response.setHeader("Content-Disposition", " attachment; filename=\"atom.xml\"");
    178                SyndFeed atom = new SyndFeedImpl();
    179                atom.setFeedType("atom_0.3");
    180                atom.getEntries().add(entry);
    181                SyndFeedOutput output = new SyndFeedOutput();
    182                output.output(atom,response.getWriter());
    183            }
    184            response.getWriter().flush();
    185        } catch( final Exception e ) {
    186            LOG.error( "Unable to generate response", e );
    187            throw new ServletException( "Internal problem - whack Janne on the head to get a better error report");
    188        }
    189    }
    190
    191    private SyndEntry getBlogEntry( final String entryid ) {
    192        final Page page = m_engine.getManager( PageManager.class ).getPage( entryid );
    193        final Page firstVersion = m_engine.getManager( PageManager.class ).getPage( entryid, 1 );
    194        final SyndEntry entry = new SyndEntryImpl();
    195        final String pageText = m_engine.getManager( PageManager.class ).getText(page.getName());
    196        final int firstLine = pageText.indexOf('\n');
    197
    198        String title = "";
    199        if( firstLine > 0 ) {
    200            title = pageText.substring( 0, firstLine );
    201        }
    202
    203        if( title.trim().isEmpty() ) {
    204            title = page.getName();
    205        }
    206
    207        // Remove wiki formatting
    208        while( title.startsWith("!") ) {
    209            title = title.substring(1);
    210        }
    211
    212        entry.setTitle( title );
    213        entry.setPublishedDate(firstVersion.getLastModified() );
    214        entry.setUpdatedDate(page.getLastModified() );
    215        entry.setAuthor( page.getAuthor());
    216        List<SyndContent> list  = new ArrayList<>();
    217        list.add(new SyndContentImpl());
    218        list.get(0).setValue(pageText);
    219
    220        return entry;
    221    }
    222
    223    /**
    224     *  Creates and outputs a full list of all available blogs
    225     */
    226    private SyndFeed listBlogs( final HttpServletRequest request ) throws ProviderException{
    227        final Collection< Page > pages = m_engine.getManager( PageManager.class ).getAllPages();
    228        final SyndFeed feed = new SyndFeedImpl();
    229        feed.setTitle("List of blogs at this site");
    230        feed.setPublishedDate( new Date() );
    231
    232        for( final Page p : pages ) {
    233            //  List only weblogs
    234            //  FIXME: Unfortunately, a weblog is not known until it has een executed once, because plugins are off during the initial startup phase.
    235            LOG.debug( p.getName() + " = " + p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) );
    236
    237            if( !( "true".equals( p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) ) ) &&
    238                    !( "true".equals( p.getAttribute( "@" + WeblogPlugin.ATTR_ISWEBLOG ) ) )) {
    239                continue;
    240            }
    241
    242            final String encodedName = TextUtil.urlEncodeUTF8( p.getName() );
    243            final Context context = Wiki.context().create( m_engine, p );
    244            final String title = TextUtil.replaceEntities( org.apache.wiki.rss.Feed.getSiteName( context ) );
    245            //FIXME this needs to be an absolute URL not a relative one
    246            final SyndLink postlink = createLink( "service.post", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/atom/" + encodedName, title );
    247            final SyndLink editlink = createLink( "service.edit", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/atom/" + encodedName, title );
    248            final SyndLink feedlink = createLink( "service.feed", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/rss.jsp?page=" + encodedName, title );
    249
    250            feed.getLinks().add( postlink );
    251            feed.getLinks().add( feedlink );
    252            feed.getLinks().add( editlink );
    253        }
    254
    255        return feed;
    256    }
    257
    258    private SyndLink createLink( final String rel, final String href, final String title ) {
    259        final SyndLink link = new SyndLinkImpl();
    260        link.setRel( rel );
    261        link.setTitle( title );
    262        link.setType( "application/x.atom+xml" );
    263        link.setHref( href );
    264
    265        return link;
    266    }
    267
    268    /**
    269     *  {@inheritDoc}
    270     */
    271    @Override
    272    public void doDelete( final HttpServletRequest request, final HttpServletResponse response ) {
    273        LOG.debug( "Received HTTP DELETE" );
    274    }
    275
    276    /**
    277     *  {@inheritDoc}
    278     */
    279    @Override
    280    public void doPut( final HttpServletRequest request, final HttpServletResponse response ) {
    281        LOG.debug( "Received HTTP PUT" );
    282    }
    283
    284}