001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/podcasts/tags/sakai_2-4-1/podcasts/src/java/org/sakaiproject/tool/podcasts/RSSPodfeedServlet.java $
003: * $Id: RSSPodfeedServlet.java 28697 2007-04-11 23:21:35Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.podcasts;
021:
022: import java.io.IOException;
023:
024: import javax.servlet.ServletException;
025: import javax.servlet.http.HttpServlet;
026: import javax.servlet.http.HttpServletRequest;
027: import javax.servlet.http.HttpServletResponse;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.apache.commons.codec.binary.Base64;
032: import org.sakaiproject.api.app.podcasts.PodfeedService;
033: import org.sakaiproject.component.cover.ComponentManager;
034: import org.sakaiproject.content.cover.ContentHostingService;
035: import org.sakaiproject.event.api.Event;
036: import org.sakaiproject.event.cover.EventTrackingService;
037: import org.sakaiproject.event.cover.NotificationService;
038: import org.sakaiproject.event.cover.UsageSessionService;
039: import org.sakaiproject.user.api.Authentication;
040: import org.sakaiproject.user.api.Evidence;
041: import org.sakaiproject.user.cover.AuthenticationManager;
042: import org.sakaiproject.util.IdPwEvidence;
043:
044: public class RSSPodfeedServlet extends HttpServlet {
045: /** Used to set the MIME type of the response back to the client **/
046: private static final String RESPONSE_MIME_TYPE = "application/xml; charset=UTF-8";
047:
048: /** Used to track the event of generating a public feed **/
049: private final String EVENT_PUBLIC_FEED = "podcast.generate.public";
050:
051: /** Used to track the event of generating a private feed **/
052: private final String EVENT_PRIVATE_FEED = "podcast.generate.private";
053:
054: /** FUTURE DEVELOPMENT: set to pass in feed type as a parameter with name 'type' **/
055: private static final String FEED_TYPE = "type";
056:
057: private PodfeedService podfeedService;
058:
059: private final Log LOG = LogFactory.getLog(RSSPodfeedServlet.class);
060:
061: /**
062: * The doGet method of the servlet. <br>
063: *
064: * This method is called when a form has its tag value method equals to get.
065: *
066: * @param request
067: * the request send by the client to the server
068: * @param response
069: * the response send by the server to the client
070: * @throws ServletException
071: * if an error occurred
072: * @throws IOException
073: * if an error occurred
074: */
075: public void doGet(final HttpServletRequest request,
076: final HttpServletResponse response)
077: throws ServletException, IOException {
078:
079: // get requested URL in attempt to extract siteId
080: String reqURL = request.getPathInfo();
081: String siteId;
082:
083: if (reqURL != null) {
084: siteId = reqURL.substring(reqURL.lastIndexOf("/") + 1);
085: } else {
086: // could not get it from request URL, try URI
087: reqURL = request.getRequestURI();
088:
089: siteId = reqURL.substring(1, reqURL.lastIndexOf("/"));
090: }
091:
092: LOG.debug("Podcast feed requested for site: " + siteId);
093:
094: // get podcast folder id to determine if public/private
095: final String podcastsCollection = podfeedService
096: .retrievePodcastFolderId(siteId);
097:
098: // if error finding podcast folder id, will return null, so return
099: // Internal Server Error message back to client
100: if (podcastsCollection == null) {
101: response
102: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
103:
104: return;
105: }
106:
107: // Determine if resource if public/private ("default" - public)
108: final boolean pubView = ContentHostingService
109: .isPubView(podcastsCollection);
110:
111: if (!pubView) {
112:
113: // if private, was username/password sent with request?
114: final Evidence e = getBasicAuthEvidence(request);
115:
116: if ((e != null)) {
117:
118: // authenticate
119: try {
120: LOG.info("Authenticating " + e);
121: final Authentication a = AuthenticationManager
122: .authenticate(e);
123:
124: if (!UsageSessionService.login(a, request)) {
125: // login failed, so ask for auth again
126: sendErrorResponse(response);
127:
128: return;
129: }
130: } catch (final Exception exc) {
131: // something went wrong, so ask for auth again
132: sendErrorResponse(response);
133:
134: return;
135: }
136: } else {
137: // user name missing, so can't authenticate, so ask for auth
138: sendErrorResponse(response);
139:
140: return;
141: }
142:
143: // Authenticated, but are they members of the site
144: // accomplished by doing a check on read access to podcast folder
145: if (!podfeedService.allowAccess(podcastsCollection)) {
146: // check to access denied error
147: response.sendError(403);
148: }
149: }
150:
151: response.setContentType(RESPONSE_MIME_TYPE);
152:
153: // generates actual feed, 2nd parameter for future to allow different
154: // feed types currently only rss_2.0 is generated
155: final String podcastFeed = podfeedService.generatePodcastRSS(
156: siteId, request.getParameter(FEED_TYPE));
157:
158: // if problem getting feed, null will be returned so return
159: // Internal Server Error to client
160: if (podcastFeed == null || podcastFeed.equals("")) {
161: response
162: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
163: } else {
164: response.getWriter().write(podcastFeed);
165:
166: // add entry for event tracking
167: Event event = null;
168: if (pubView) {
169: event = EventTrackingService.newEvent(
170: EVENT_PUBLIC_FEED, podcastsCollection, false,
171: NotificationService.NOTI_NONE);
172: } else {
173: event = EventTrackingService.newEvent(
174: EVENT_PRIVATE_FEED, podcastsCollection, false,
175: NotificationService.NOTI_NONE);
176: }
177: EventTrackingService.post(event);
178:
179: }
180:
181: }
182:
183: /**
184: * The doPost method of the servlet. <br>
185: *
186: * This method is called when a form has its tag value method equals to
187: * post.
188: *
189: * @param request
190: * the request send by the client to the server
191: * @param response
192: * the response send by the server to the client
193: * @throws ServletException
194: * if an error occurred
195: * @throws IOException
196: * if an error occurred
197: */
198: public void doPost(final HttpServletRequest request,
199: final HttpServletResponse response)
200: throws ServletException, IOException {
201:
202: doGet(request, response);
203: }
204:
205: /**
206: * Initialization of the servlet. <br>
207: *
208: * @throws ServletException
209: * if an error occure
210: */
211: public void init() throws ServletException {
212: LOG.debug(this + ": RSSPodfeedServlet.init()");
213:
214: podfeedService = (PodfeedService) ComponentManager
215: .get("org.sakaiproject.api.app.podcasts.PodfeedService");
216:
217: if (podfeedService == null)
218: throw new ServletException(new IllegalStateException(
219: "podfeedService == null"));
220: }
221:
222: /**
223: * @param podfeedService
224: * The podfeedService to set.
225: */
226: public void setPodfeedService(final PodfeedService podfeedService) {
227: this .podfeedService = podfeedService;
228: }
229:
230: /**
231: * Extracts auth information if it exists in the HTTP headers
232: *
233: * @param request
234: * The HttpRequest object to be searched
235: *
236: * @return IdPwEvidence
237: * Contains the auth information if found in request headers or null if not found
238: */
239: private IdPwEvidence getBasicAuthEvidence(
240: final HttpServletRequest request) {
241:
242: Base64 base64Encoder = new Base64();
243:
244: final String header = request.getHeader("Authorization");
245: String[] elements = null;
246:
247: LOG.debug("Authorization: " + header);
248:
249: if (header != null)
250: elements = header.split(" ");
251:
252: if (elements != null && elements.length >= 2) {
253:
254: final String type = elements[0];
255: final String hash = elements[1];
256:
257: LOG.debug("type: " + type + " hash: " + hash);
258:
259: final String[] credential = (new String(base64Encoder
260: .decode(hash.getBytes()))).split(":");
261:
262: LOG.debug("credential: " + credential);
263:
264: if (credential != null && credential.length >= 2) {
265: final String eid = credential[0];
266: final String password = credential[1];
267:
268: LOG.debug("eid: " + eid + " password: ********");
269:
270: if ((eid.length() == 0) || (password.length() == 0)) {
271: return null;
272: }
273:
274: return new IdPwEvidence(eid, password);
275:
276: }
277:
278: }
279:
280: return null;
281:
282: }
283:
284: /**
285: * If missing or invalid username/password given, return HTTP 401 to request
286: * authentication.
287: *
288: * @param response
289: * The Response object so we can set headers
290: *
291: * @throws IOException
292: * Throw this exception back if there was a problem setting the headers
293: */
294: private void sendErrorResponse(final HttpServletResponse response)
295: throws IOException {
296: response.setHeader("WWW-Authenticate",
297: HttpServletRequest.BASIC_AUTH + " realm=\"Podcaster\"");
298: response.sendError(401);
299:
300: }
301:
302: }
|