mraible commented on code in PR #154:
URL: https://github.com/apache/roller/pull/154#discussion_r4239807986


##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  The ASF licenses this file to You
+ * under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.  For additional information regarding
+ * copyright in this work, please see the NOTICE file in the top level
+ * directory of this distribution.
+ */
+package org.apache.roller.weblogger.webservices.atomprotocol;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.Locale;
+
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.jdom2.Document;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.rometools.propono.atom.common.AtomService;
+import com.rometools.propono.atom.common.Categories;
+import com.rometools.propono.atom.server.AtomException;
+import com.rometools.propono.atom.server.AtomHandler;
+import com.rometools.propono.atom.server.AtomMediaResource;
+import com.rometools.propono.atom.server.AtomRequest;
+import com.rometools.rome.feed.atom.Content;
+import com.rometools.rome.feed.atom.Entry;
+import com.rometools.rome.feed.atom.Feed;
+import com.rometools.rome.feed.atom.Link;
+import com.rometools.rome.io.WireFeedOutput;
+import com.rometools.rome.io.impl.Atom10Generator;
+import com.rometools.rome.io.impl.Atom10Parser;
+import org.apache.roller.weblogger.util.Utilities;
+
+/**
+ * Forked from rome-propono's AtomServlet to remove the dependency on propono's
+ * servlet class, which has no Jakarta-compatible release. This servlet 
directly
+ * creates a {@link RollerAtomHandler} instead of going through propono's
+ * AtomHandlerFactory/FactoryFinder lookup.
+ *
+ * <p>Handles Atom Publishing Protocol requests by parsing incoming XML into
+ * ROME Atom {@link Entry} objects, passing those to the handler, and
+ * serializing entries and feeds returned by the handler to the response.</p>
+ */
+public class RollerAtomServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    /** Feed type supported by this servlet */
+    public static final String FEED_TYPE = "atom_1.0";
+
+    private static String contextDirPath = null;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(RollerAtomServlet.class);
+
+    static {
+        Atom10Parser.setResolveURIs(true);
+    }
+
+    /**
+     * Create an Atom request handler directly, bypassing propono's factory 
lookup.
+     */
+    private AtomHandler createAtomRequestHandler(final HttpServletRequest 
request, final HttpServletResponse response) {
+        return new RollerAtomHandler(request, response);
+    }
+
+    /**
+     * Handles an Atom GET by calling handler and writing results to response.
+     */
+    @Override
+    protected void doGet(final HttpServletRequest req, final 
HttpServletResponse res) throws ServletException, IOException {
+        LOG.debug("Entering");
+        final AtomHandler handler = createAtomRequestHandler(req, res);
+        final String userName = handler.getAuthenticatedUsername();
+        if (userName != null) {
+            final AtomRequest areq = new RollerAtomRequestImpl(req);
+            try {
+                if (handler.isAtomServiceURI(areq)) {
+                    // return an Atom Service document
+                    final AtomService service = handler.getAtomService(areq);
+                    final Document doc = service.serviceToDocument();
+                    res.setContentType("application/atomsvc+xml; 
charset=utf-8");
+                    final Writer writer = res.getWriter();
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.setFormat(Format.getPrettyFormat());
+                    outputter.output(doc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isCategoriesURI(areq)) {
+                    final Categories cats = handler.getCategories(areq);
+                    res.setContentType("application/xml");
+                    final Writer writer = res.getWriter();
+                    final Document catsDoc = new Document();
+                    catsDoc.setRootElement(cats.categoriesToElement());
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.output(catsDoc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isCollectionURI(areq)) {
+                    // return a collection
+                    final Feed col = handler.getCollection(areq);
+                    col.setFeedType(FEED_TYPE);
+                    final WireFeedOutput wireFeedOutput = new WireFeedOutput();
+                    final Document feedDoc = wireFeedOutput.outputJDom(col);
+                    res.setContentType("application/atom+xml; charset=utf-8");
+                    final Writer writer = res.getWriter();
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.setFormat(Format.getPrettyFormat());
+                    outputter.output(feedDoc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isEntryURI(areq)) {
+                    // return an entry
+                    final Entry entry = handler.getEntry(areq);
+                    if (entry != null) {
+                        res.setContentType("application/atom+xml; type=entry; 
charset=utf-8");
+                        final Writer writer = res.getWriter();
+                        Atom10Generator.serializeEntry(entry, writer);
+                        writer.close();
+                    } else {
+                        res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                    }
+                } else if (handler.isMediaEditURI(areq)) {
+                    final AtomMediaResource entry = 
handler.getMediaResource(areq);
+                    res.setContentType(entry.getContentType());
+                    res.setContentLength((int) entry.getContentLength());
+                    Utilities.copyInputToOutput(entry.getInputStream(), 
res.getOutputStream());

Review Comment:
   After merging master, this servlet is master's StAX `RollerAtomServlet` from 
#210 rather than the Propono fork. It serves media through 
`MediaCollection.createMediaResource`, which applies 
`MediaTypePolicy.applyResponseHeaders`, so responses get `nosniff` and types 
that aren't safe inline are sent as attachments, the same as `ResourceServlet`. 
CodeQL passes on the new head.



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  The ASF licenses this file to You
+ * under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.  For additional information regarding
+ * copyright in this work, please see the NOTICE file in the top level
+ * directory of this distribution.
+ */
+package org.apache.roller.weblogger.webservices.atomprotocol;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.Locale;
+
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.jdom2.Document;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.rometools.propono.atom.common.AtomService;
+import com.rometools.propono.atom.common.Categories;
+import com.rometools.propono.atom.server.AtomException;
+import com.rometools.propono.atom.server.AtomHandler;
+import com.rometools.propono.atom.server.AtomMediaResource;
+import com.rometools.propono.atom.server.AtomRequest;
+import com.rometools.rome.feed.atom.Content;
+import com.rometools.rome.feed.atom.Entry;
+import com.rometools.rome.feed.atom.Feed;
+import com.rometools.rome.feed.atom.Link;
+import com.rometools.rome.io.WireFeedOutput;
+import com.rometools.rome.io.impl.Atom10Generator;
+import com.rometools.rome.io.impl.Atom10Parser;
+import org.apache.roller.weblogger.util.Utilities;
+
+/**
+ * Forked from rome-propono's AtomServlet to remove the dependency on propono's
+ * servlet class, which has no Jakarta-compatible release. This servlet 
directly
+ * creates a {@link RollerAtomHandler} instead of going through propono's
+ * AtomHandlerFactory/FactoryFinder lookup.
+ *
+ * <p>Handles Atom Publishing Protocol requests by parsing incoming XML into
+ * ROME Atom {@link Entry} objects, passing those to the handler, and
+ * serializing entries and feeds returned by the handler to the response.</p>
+ */
+public class RollerAtomServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    /** Feed type supported by this servlet */
+    public static final String FEED_TYPE = "atom_1.0";
+
+    private static String contextDirPath = null;
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(RollerAtomServlet.class);
+
+    static {
+        Atom10Parser.setResolveURIs(true);
+    }
+
+    /**
+     * Create an Atom request handler directly, bypassing propono's factory 
lookup.
+     */
+    private AtomHandler createAtomRequestHandler(final HttpServletRequest 
request, final HttpServletResponse response) {
+        return new RollerAtomHandler(request, response);
+    }
+
+    /**
+     * Handles an Atom GET by calling handler and writing results to response.
+     */
+    @Override
+    protected void doGet(final HttpServletRequest req, final 
HttpServletResponse res) throws ServletException, IOException {
+        LOG.debug("Entering");
+        final AtomHandler handler = createAtomRequestHandler(req, res);
+        final String userName = handler.getAuthenticatedUsername();
+        if (userName != null) {
+            final AtomRequest areq = new RollerAtomRequestImpl(req);
+            try {
+                if (handler.isAtomServiceURI(areq)) {
+                    // return an Atom Service document
+                    final AtomService service = handler.getAtomService(areq);
+                    final Document doc = service.serviceToDocument();
+                    res.setContentType("application/atomsvc+xml; 
charset=utf-8");
+                    final Writer writer = res.getWriter();
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.setFormat(Format.getPrettyFormat());
+                    outputter.output(doc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isCategoriesURI(areq)) {
+                    final Categories cats = handler.getCategories(areq);
+                    res.setContentType("application/xml");
+                    final Writer writer = res.getWriter();
+                    final Document catsDoc = new Document();
+                    catsDoc.setRootElement(cats.categoriesToElement());
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.output(catsDoc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isCollectionURI(areq)) {
+                    // return a collection
+                    final Feed col = handler.getCollection(areq);
+                    col.setFeedType(FEED_TYPE);
+                    final WireFeedOutput wireFeedOutput = new WireFeedOutput();
+                    final Document feedDoc = wireFeedOutput.outputJDom(col);
+                    res.setContentType("application/atom+xml; charset=utf-8");
+                    final Writer writer = res.getWriter();
+                    final XMLOutputter outputter = new XMLOutputter();
+                    outputter.setFormat(Format.getPrettyFormat());
+                    outputter.output(feedDoc, writer);
+                    writer.close();
+                    res.setStatus(HttpServletResponse.SC_OK);
+                } else if (handler.isEntryURI(areq)) {
+                    // return an entry
+                    final Entry entry = handler.getEntry(areq);
+                    if (entry != null) {
+                        res.setContentType("application/atom+xml; type=entry; 
charset=utf-8");
+                        final Writer writer = res.getWriter();
+                        Atom10Generator.serializeEntry(entry, writer);
+                        writer.close();
+                    } else {
+                        res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                    }
+                } else if (handler.isMediaEditURI(areq)) {
+                    final AtomMediaResource entry = 
handler.getMediaResource(areq);
+                    res.setContentType(entry.getContentType());
+                    res.setContentLength((int) entry.getContentLength());
+                    Utilities.copyInputToOutput(entry.getInputStream(), 
res.getOutputStream());
+                    res.getOutputStream().flush();
+                    res.getOutputStream().close();
+                } else {
+                    res.setStatus(HttpServletResponse.SC_NOT_FOUND);
+                }
+            } catch (final AtomException ae) {
+                res.sendError(ae.getStatus(), ae.getMessage());
+                LOG.debug("An error occurred while processing GET", ae);
+            } catch (final Exception e) {
+                res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
e.getMessage());
+                LOG.debug("An error occurred while processing GET", e);
+            }
+        } else {
+            res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
+            res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        }
+        LOG.debug("Exiting");
+    }
+
+    /**
+     * Handles an Atom POST by calling handler to identify URI, reading/parsing
+     * data, calling handler and writing results to response.
+     */
+    @Override
+    protected void doPost(final HttpServletRequest req, final 
HttpServletResponse res) throws ServletException, IOException {
+        LOG.debug("Entering");
+        final AtomHandler handler = createAtomRequestHandler(req, res);
+        final String userName = handler.getAuthenticatedUsername();
+        if (userName != null) {
+            final AtomRequest areq = new RollerAtomRequestImpl(req);
+            try {
+                if (handler.isCollectionURI(areq)) {
+
+                    final String contentType = req.getContentType();
+                    if (contentType != null && 
contentType.startsWith("application/atom+xml")) {
+
+                        // parse incoming entry
+                        final Entry entry = Atom10Parser.parseEntry(

Review Comment:
   Merging master brought in the AtomPub server from #210/#212, which already 
includes #198: `webservices.enableAtomPub` is checked on every request (404 
when off), entry bodies are capped by `webservices.atomPubMaxEntrySize` (413), 
and entries are parsed only after authentication. Parsing uses master's 
`AtomReader` (StAX with DTDs and external entities disabled) instead of 
`SafeSAXBuilder`, since master moved to StAX and this branch now matches it. On 
top of master's code, this branch only switches to `jakarta.servlet` and 
removes the OAuth 1.0a path from `RollerAtomHandler`. `RollerAtomServletTest` 
and `GlobalConfigAtomPubLimitTest` came across and pass.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to