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}