001: /**
002: * $Id: RuleSetServlet.java,v 1.16 2004/03/24 00:05:57 ru111118 Exp $
003: * Copyright 2002 Sun Microsystems, Inc. All
004: * rights reserved. Use of this product is subject
005: * to license terms. Federal Acquisitions:
006: * Commercial Software -- Government Users
007: * Subject to Standard License Terms and
008: * Conditions.
009: *
010: * Sun, Sun Microsystems, the Sun logo, and iPlanet
011: * are trademarks or registered trademarks of Sun Microsystems,
012: * Inc. in the United States and other countries.
013: */package com.sun.portal.rewriter.admin;
014:
015: import javax.servlet.ServletException;
016: import javax.servlet.ServletInputStream;
017: import javax.servlet.RequestDispatcher;
018: import javax.servlet.http.HttpServlet;
019: import javax.servlet.http.HttpServletRequest;
020: import javax.servlet.http.HttpServletResponse;
021:
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.io.ByteArrayOutputStream;
025: import java.util.Locale;
026: import java.util.Hashtable;
027:
028: import com.iplanet.jato.util.Encoder;
029:
030: import com.iplanet.sso.SSOToken;
031: import com.iplanet.sso.SSOTokenManager;
032: import com.iplanet.sso.SSOException;
033: import com.iplanet.am.sdk.AMRole;
034:
035: import com.iplanet.am.util.Debug;
036: import com.iplanet.am.util.SystemProperties;
037:
038: import com.sun.portal.rewriter.admin.model.RewriterModel;
039: import com.sun.portal.rewriter.admin.model.RewriterModelImpl;
040: import com.sun.portal.rewriter.util.xml.Document;
041:
042: import com.iplanet.am.console.base.model.AMAdminTypes;
043: import com.iplanet.am.console.base.model.AMI18NUtils;
044:
045: import com.sun.identity.common.Constants;
046:
047: public class RuleSetServlet extends HttpServlet {
048:
049: private static final String RWSERVLET_SESSION_ATTR = "ps-rwadmin-current-upload-ruleset";
050:
051: protected static String loginURL = SystemProperties
052: .get(Constants.AM_SERVER_PROTOCOL)
053: + "://"
054: + SystemProperties.get(Constants.AM_SERVER_HOST)
055: + ":"
056: + SystemProperties.get(Constants.AM_SERVER_PORT)
057: + SystemProperties
058: .get(Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR)
059: + "/UI/Login";
060:
061: // All module servlets use this debug object.
062: private static Debug debug = Debug.getInstance("amConsole");
063:
064: private static int TYPE_ERROR = 0;
065: private static int TYPE_WARNING = 1;
066: private static int TYPE_INFORMATION = 2;
067:
068: private SSOToken ssoToken;
069: private String psDeployURI;
070:
071: protected void service(HttpServletRequest req,
072: HttpServletResponse resp) throws ServletException,
073: IOException {
074: String reqURI = req.getRequestURI();
075: psDeployURI = reqURI.substring(0, reqURI.indexOf('/', 1));
076:
077: try {
078: ssoToken = checkAuthentication(req);
079: int userType = new AMAdminTypes(ssoToken).getUserType();
080: if (userType != AMRole.TOP_LEVEL_ADMIN_ROLE) {
081: RewriterModel rwm = new RewriterModelImpl(req);
082: String msgStr = showMessage(
083: TYPE_ERROR,
084: rwm
085: .getLocalizedString("notsuperadminmsgbox.title"),
086: rwm
087: .getLocalizedString("notsuperadminmsgbox.msg"),
088: null);
089: Locale locale = rwm.getUserLocale();
090: String agentType = rwm.getClientType();
091: String contentType = AMI18NUtils
092: .getContentType(agentType);
093: String charset = AMI18NUtils.getCharset(agentType,
094: locale);
095: resp
096: .setContentType(contentType + ";charset="
097: + charset);
098: PrintWriter out = resp.getWriter();
099: out = resp.getWriter();
100: out.write(msgStr);
101: out.close();
102: } else {
103: super .service(req, resp);
104: }
105:
106: } catch (SSOException soe) {
107: try {
108: resp.sendRedirect(loginURL);
109: return;
110: } catch (IOException ioe) {
111: throw new ServletException("Cannot redirect to "
112: + loginURL);
113: }
114: }
115: }
116:
117: /**
118: * Checks if the user is authenticated, that is, if SSOToken is available
119: * and whether the token is still valid, else throws SSOException.
120: *
121: * @param request The HttpServletRequest object.
122: * @return The valid SSOToken.
123: * @throws SSOException
124: */
125: private SSOToken checkAuthentication(HttpServletRequest request)
126: throws SSOException {
127: SSOTokenManager manager = SSOTokenManager.getInstance();
128: SSOToken ssoToken = manager.createSSOToken(request);
129: manager.getInstance().validateToken(ssoToken);
130: return ssoToken;
131: }
132:
133: /**
134: * Gets values stored in the session
135: * @param name Key of the values to be retrived from the session
136: * @return a List of values from the session
137: */
138: public String getFromSession(String name) {
139: String value = "";
140: try {
141: value = ssoToken.getProperty(name);
142: } catch (SSOException ssoe) {
143: debug.error("RuleSetServlet.getFromSession() ", ssoe);
144: }
145: return value;
146: }
147:
148: /**
149: * Stores values in the session
150: * @param name Key of the value to be stored in the session
151: * @param values value to be stored
152: */
153: public void storeToSession(String name, String values) {
154: if ((name != null) && (!name.equals("")) && (values != null)) {
155: try {
156: ssoToken.setProperty(name, values);
157: } catch (SSOException ssoe) {
158: debug.error("RuleSetServlet.storeToSession() ", ssoe);
159: }
160: }
161: }
162:
163: /**Process the HTTP Get request*/
164: public void doGet(HttpServletRequest request,
165: HttpServletResponse response) throws ServletException,
166: IOException {
167:
168: RewriterModel model = new RewriterModelImpl(request);
169: Locale locale = model.getUserLocale();
170: String agentType = model.getClientType();
171: String contentType = AMI18NUtils.getContentType(agentType);
172: String encoding = AMI18NUtils.getCharset(agentType, locale);
173: String encodedRuleSetName = request.getParameter("RuleSetName");
174: String action = request.getParameter("action");
175: if ("NEW".equals(action)) {
176: showUploadJsp(response, contentType, encoding, request,
177: model);
178: return;
179: }
180:
181: try {
182: if (encoding == null || encodedRuleSetName == null) {
183: debug
184: .error("RuleSetServlet.doGet :: Illegal Parameters");
185: return;
186: }
187: byte[] decodedBytes = Encoder.decode(encodedRuleSetName);
188: String ruleSetName = new String(decodedBytes, encoding);
189:
190: if (action != null && action.equalsIgnoreCase("getRuleSet")) {
191: response.setContentType("application/x-xxxxx; charset="
192: + encoding);
193: response.setHeader("Content-Disposition",
194: "filename=\"RuleSetXML\"");
195: PrintWriter out = response.getWriter();
196: out = response.getWriter();
197: out.write(model.getRulesetXML(ruleSetName));
198: out.close();
199: } else {
200: storeToSession(RWSERVLET_SESSION_ATTR, ruleSetName);
201: showUploadJsp(response, contentType, encoding, request,
202: model);
203: }
204:
205: } catch (Exception e) {
206: debug
207: .error("RuleSetServlet.doPost :: Illegal form encoding type");
208: }
209: }
210:
211: private void showUploadJsp(HttpServletResponse response,
212: String aContentType, String aEncoding,
213: HttpServletRequest request, RewriterModel aModel)
214: throws ServletException, IOException {
215: response.setContentType(aContentType + ";charset=" + aEncoding);
216: request.setAttribute("pageTitle", aModel
217: .getLocalizedString("HTMLPage.title"));
218: request.setAttribute("rewriterLabel", aModel
219: .getLocalizedString("popup.label.rewriter"));
220: request.setAttribute("ruleSetLabel", aModel
221: .getLocalizedString("popup.label.rulesetxml"));
222: request.setAttribute("selectFileLabel", aModel
223: .getLocalizedString("popup.label.selectFile"));
224: request.setAttribute("uploadLabel", aModel
225: .getLocalizedString("popup.button.upload"));
226: request.setAttribute("closeLabel", aModel
227: .getLocalizedString("popup.button.close"));
228: request.setAttribute("psDeployURI", psDeployURI);
229: RequestDispatcher rd = request
230: .getRequestDispatcher("/ps/rwadmin/UploadRuleSet.jsp");
231: rd.include(request, response);
232: }
233:
234: /**Process the HTTP Post request*/
235: public void doPost(HttpServletRequest req,
236: HttpServletResponse response) throws ServletException,
237: IOException {
238: Hashtable postArgs = null;
239: String msgMarkup = "";
240:
241: // Get the Rewriter model
242: RewriterModel model = new RewriterModelImpl(req);
243: Locale locale = model.getUserLocale();
244: String agentType = model.getClientType();
245: String contentType = AMI18NUtils.getContentType(agentType);
246: String encoding = AMI18NUtils.getCharset(agentType, locale);
247:
248: String ruleSetName = getFromSession(RWSERVLET_SESSION_ATTR);
249: if (ruleSetName == null)
250: ruleSetName = "NEW";
251:
252: try {
253: if (req.getContentType().toLowerCase().startsWith(
254: "multipart/form-data")) {
255:
256: postArgs = parseMultiPartFormData(req.getContentType(),
257: req.getContentLength(), req.getInputStream());
258: UploadedFile uf = (UploadedFile) postArgs.get("files");
259: if (ruleSetName != null && uf != null
260: && uf.filename != null
261: && uf.filename.length() > 0
262: && uf.content.length > 0) {
263: String xmlEncoding = Document
264: .parseEncoding(uf.content);
265: String editedRuleSetXML = new String(uf.content,
266: xmlEncoding);
267:
268: if (!model.isEditOverwrite(ruleSetName,
269: editedRuleSetXML)) {
270: model.saveRulesetXML(editedRuleSetXML, true);
271: msgMarkup = showMessage(
272: TYPE_INFORMATION,
273: model
274: .getLocalizedString("popup.uploadsuccess.title"),
275: model
276: .getLocalizedString("popup.uploadsuccess.msg"),
277: model);
278: } else {
279: msgMarkup = showMessage(
280: TYPE_ERROR,
281: model
282: .getLocalizedString("popup.uploadfailed.title"),
283: model
284: .getLocalizedString("renameruleerr.msg"),
285: model);
286: }
287: } else {
288: msgMarkup = showMessage(
289: TYPE_ERROR,
290: model
291: .getLocalizedString("popup.uploadfailed.title"),
292: model
293: .getLocalizedString("popup.nocontent.msg"),
294: model);
295: }
296:
297: } else {
298: debug
299: .error("RuleSetServlet.doPost :: Illegal form encoding type");
300: msgMarkup = showMessage(
301: TYPE_ERROR,
302: model
303: .getLocalizedString("popup.uploadfailed.title"),
304: model.getLocalizedString("popup.nocontent.msg"),
305: model);
306: }
307:
308: } catch (Exception e) {
309: debug.error("RuleSetServlet.doPost ::", e);
310: msgMarkup = showMessage(TYPE_ERROR, model
311: .getLocalizedString("popup.uploadfailed.title"),
312: model.getLocalizedString(e.getMessage()), model);
313: }
314:
315: response.setContentType(contentType + ";charset=" + encoding);
316: PrintWriter out = response.getWriter();
317: out.write(msgMarkup);
318: out.close();
319: }
320:
321: class UploadedFile {
322: public String name;
323: public String filename;
324: public String contentType;
325: public byte[] content;
326: }
327:
328: /* This method parses the input, and returns a hashtable of either
329: * String[] values (for parameters) or UploadedFile values (for
330: * files uploaded). The upload's size is capped, since otherwise
331: * a denial of service attack on server memory becomes trivial.
332: **/
333:
334: public Hashtable parseMultiPartFormData(String reqContentType,
335: int len, ServletInputStream in) throws IOException,
336: ServletException {
337: int ind = reqContentType.indexOf("boundary=");
338: String boundary = null;
339: if (ind == -1
340: || (boundary = reqContentType.substring(ind + 9)) == null) {
341: throw new ServletException(
342: "Missing or invalid boundary parameter in content type");
343: }
344:
345: int buffSize = 1024 * 8; // 8K
346: Hashtable hash = new Hashtable();
347: int result;
348: String line;
349: String lowerline;
350: String boundaryStr = "--" + boundary;
351: byte boundaryBuf[] = ("\r\n" + boundaryStr).getBytes();
352: ByteArrayOutputStream content;
353: String filename;
354: String contentType;
355: String name;
356:
357: byte[] b = new byte[buffSize];
358:
359: result = in.readLine(b, 0, b.length);
360: if (result == -1)
361: throw new IllegalArgumentException("InputStream truncated");
362: len -= result;
363:
364: line = new String(b, 0, result, "ISO-8859-1");
365: if (!line.startsWith(boundaryStr))
366: throw new IllegalArgumentException(
367: "MIME boundary missing: " + line);
368:
369: while (len > 0) {
370: // Some initialization
371: filename = null;
372: contentType = null;
373: content = new ByteArrayOutputStream();
374: name = null;
375:
376: // get next line (should be content disposition)
377: result = in.readLine(b, 0, b.length);
378: if (result == -1)
379: return hash;
380: len -= result;
381: line = new String(b, 0, result - 2, "ISO-8859-1");
382: lowerline = line.toLowerCase();
383: if (!lowerline.startsWith("content-disposition"))
384: // don't know what to do, so we'll keep looking...
385: continue;
386: // determine what the disposition is
387: ind = lowerline.indexOf("content-disposition: ");
388: int ind2 = lowerline.indexOf(";");
389: if (ind == -1 || ind2 == -1)
390: throw new IllegalArgumentException(
391: "Content Disposition line misformatted: "
392: + line);
393: String disposition = lowerline.substring(ind + 21, ind2);
394: if (!disposition.equals("form-data"))
395: throw new IllegalArgumentException(
396: "Content Disposition of " + disposition
397: + " is not supported");
398: // determine what the name is
399: int ind3 = lowerline.indexOf("name=\"", ind2);
400: int ind4 = lowerline.indexOf("\"", ind3 + 7);
401: if (ind3 == -1 || ind4 == -1)
402: throw new IllegalArgumentException(
403: "Content Disposition line misformatted: "
404: + line);
405: name = line.substring(ind3 + 6, ind4);
406: // determine filename, if any
407: int ind5 = lowerline.indexOf("filename=\"", ind4 + 2);
408: int ind6 = lowerline.indexOf("\"", ind5 + 10);
409: if (ind5 != -1 && ind6 != -1) {
410: filename = line.substring(ind5 + 10, ind6);
411: }
412:
413: // Whew! We now move onto the next line, which
414: // will either be blank, or Content-Type, followed by blank.
415: result = in.readLine(b, 0, b.length);
416: if (result == -1)
417: return hash;
418: len -= result;
419: line = new String(b, 0, result - 2, "ISO-8859-1"); // -2 to remove \r\n
420: lowerline = line.toLowerCase();
421: if (lowerline.startsWith("content-type")) {
422: int ind7 = lowerline.indexOf(" ");
423: if (ind7 == -1)
424: throw new IllegalArgumentException(
425: "Content-Type line misformatted: " + line);
426: contentType = lowerline.substring(ind7 + 1);
427: // read blank header line
428: result = in.readLine(b, 0, b.length);
429: if (result == -1)
430: return hash;
431: len -= result;
432: line = new String(b, 0, result - 2, "ISO-8859-1"); // -2 to remove \r\n
433: if (line.length() != 0) {
434: throw new IllegalArgumentException(
435: "Unexpected line in MIMEpart header: "
436: + line);
437: }
438: } else if (line.length() != 0) {
439: throw new IllegalArgumentException(
440: "Misformatted line following disposition: "
441: + line);
442: }
443:
444: //read content, implement readahead by one line
445: boolean readingContent = true;
446:
447: byte buf[] = new byte[boundaryBuf.length];
448: int i;
449:
450: // NOTE: the loop below uses a byte array (c) rather than just a byte to
451: // work-around a bug in the NES ServletInputStream.read() function. It
452: // returns -1 if there is a 255 in the input stream.
453: byte c[] = new byte[1];
454:
455: result = in.read(c);
456: while (readingContent) {
457: if (result == -1) {
458: //debug("WHOOPS - saw EOF while reading content");
459: return hash; // reach EOF
460: }
461: len -= result;
462: if (c[0] == '\r') {
463: // we might be at the end of the content so read into buf until we find out
464: buf[0] = c[0];
465: for (i = 1; i < boundaryBuf.length
466: && (result = in.read(c)) != -1
467: && c[0] == boundaryBuf[i]; i++) {
468: buf[i] = c[0];
469: len -= result;
470: }
471: if (i == boundaryBuf.length) {
472: // we read in the boundary - we're done, everything in buf is
473: // the boundary, so don't put it in content
474: readingContent = false;
475: // following the boundary is either two more -- and CR/NL if this is the
476: // last boundary, or just a CR/NL
477: in.read(c);
478: if (c[0] == '-') {
479: in.read(c); // read other dash
480: in.read(c); // read CR
481: len -= 2;
482: }
483: in.read(c); // read NL
484: len -= 2;
485: } else if (result == -1) {
486: //debug("WHOOPS - saw EOF while reading content");
487: // we reached end of file
488: return hash;
489: } else {
490: // false alarm - haven't seen boundary yet
491: content.write(buf, 0, i);
492: }
493: } else {
494: content.write(c);
495: result = in.read(c);
496: }
497: }
498:
499: //now set appropriate variable, populate hashtable
500: if (filename == null) {
501: if (hash.get(name) == null) {
502: String[] values = new String[1];
503: values[0] = content.toString("UTF-8");
504: hash.put(name, values);
505: } else {
506: Object prevobj = hash.get(name);
507: if (prevobj instanceof String[]) {
508: String[] prev = (String[]) prevobj;
509: String[] newStr = new String[prev.length + 1];
510: System.arraycopy(prev, 0, newStr, 0,
511: prev.length);
512: newStr[prev.length] = content.toString("UTF-8");
513: hash.put(name, newStr);
514: } else {
515: //now what? I think this breaks the standard.
516: throw new IllegalArgumentException(
517: "failure in parseMulti hashtable building code");
518: }
519: }
520: } else {
521: // Yes, we don't return Hashtable[] for multiple files of same name. AFAIK that's not allowed.
522: UploadedFile uf = new UploadedFile();
523: uf.name = name;
524: uf.filename = filename;
525: if (contentType == null)
526: contentType = "application/octet-stream";
527: uf.contentType = contentType;
528: uf.content = content.toByteArray();
529: hash.put(name, uf);
530: }
531: }
532: return hash;
533: }
534:
535: private String showMessage(int type, String title, String message,
536: RewriterModel model) {
537: StringBuffer buffer = new StringBuffer();
538: String cssURI = psDeployURI + "/console/css";
539: String imageURI = psDeployURI + "/console/images";
540:
541: buffer
542: .append("\n<html>")
543: .append("\n<head>")
544: //.append("\n<link rel=\"stylesheet\" href=\"" + cssURI + "/cccss_generic.css\">") //bug#4892660
545: //.append("\n<link rel=\"stylesheet\" href=\"" + cssURI + "/master-style.css\">")
546: .append(
547: "\n<link rel=\"stylesheet\" href=\"" + cssURI
548: + "/adminstyle.css\">")
549: .append("\n</head>")
550: .append("\n<body>")
551:
552: .append(
553: "\n<table align=center border=\"0\" cellspacing=\"0\" ")
554: .append("cellpadding=\"10\">")
555: .append("\n<tr>\n<td>")
556: .append("\n<table border=\"0\" cellspacing=\"0\" ")
557: .append("cellpadding=\"2\" ")
558: .append("class=\"")
559: .append(getBorderStyle(type))
560: .append("\"")
561: .append(">")
562: .append("\n<tr>")
563: .append("\n<td>")
564: .append("\n<table cellspacing=\"0\" cellpadding=\"5\" ")
565: .append("border=\"0\" class=\"").append(
566: getContentStyle(type)).append("\">").append(
567: "\n<tr>").append(
568: "\n<td valign=\"top\"><img src=\"").append(
569: getTypeIcon(type, imageURI)).append(
570: "\" width=\"32\" height=\"32\" border=\"0\">")
571: .append("\n</td>").append("\n<td>").append(
572: "\n<div class=\"alert-header-text\"><b>")
573: .append(title).append("</b></div>").append(
574: "\n<div class=\"alert-normal-text\">").append(
575: message).append("</div>")
576:
577: .append("\n</td>").append("\n</tr>").append(
578: "\n</table>").append("\n</td>").append(
579: "\n</tr>").append("\n</table>").append(
580: "\n</td>").append("\n</tr>").append(
581: "\n</table>");
582:
583: if (model != null) {
584: String backLabel = model
585: .getLocalizedString("popup.button.back");
586: String closeLabel = model
587: .getLocalizedString("popup.button.close");
588:
589: buffer
590: .append(
591: "\n<table align=center border=0 cellpadding=5 cellspacing=2>")
592: .append("\n<tr>")
593:
594: .append("\n<td align=right>")
595: .append(
596: "\n<table border=0 cellpadding=1 cellspacing=0 class=\"button-frame-enabled\">")
597: .append("\n<tr><td>")
598: .append(
599: "\n<table border=0 cellpadding=0 cellspacing=0 width=\"100%\" class=\"button-content-enabled\">")
600: .append("\n<tr>")
601: .append("\n<td align=\"center\" nowrap>")
602: .append(
603: "\n<a href=\"javascript:window.history.back()\" class=\"button-link\">")
604: .append(
605: "\n<span class=\"button-link-enabled-text\">")
606: .append(backLabel)
607: .append("\n</span>")
608: .append("\n</a></td></tr>")
609: .append("\n</table>")
610: .append("\n</td></tr>")
611: .append("\n</table>")
612: .append("\n</td>")
613:
614: .append("\n<td align=left>")
615: .append(
616: "\n<table border=0 cellpadding=1 cellspacing=0 class=\"button-frame-enabled\">")
617: .append("\n<tr><td>")
618: .append(
619: "\n<table border=0 cellpadding=0 cellspacing=0 width=\"100%\" class=\"button-content-enabled\">")
620: .append("\n<tr>")
621: .append("\n<td align=\"center\" nowrap>")
622: .append(
623: "\n<a href=\"javascript:window.close()\" class=\"button-link\">")
624: .append(
625: "\n<span class=\"button-link-enabled-text\">")
626: .append(closeLabel).append("\n</span>").append(
627: "\n</a></td></tr>").append("\n</table>")
628: .append("\n</td></tr>").append("\n</table>")
629: .append("\n</td>")
630:
631: .append("\n</tr>").append("\n</table>");
632: }
633:
634: buffer.append("\n</body>").append("\n</html>");
635:
636: return buffer.toString();
637: }
638:
639: /**
640: * gets icon image
641: *
642: * @param type of message box
643: * @param URI of image
644: * @return icon image
645: */
646: protected String getTypeIcon(int type, String URI) {
647: String image = null;
648:
649: if (type == TYPE_WARNING) {
650: image = URI + "/warning_32_sunplex.gif";
651: } else if (type == TYPE_INFORMATION) {
652: image = URI + "/info_32_sunplex.gif";
653: } else {
654: image = URI + "/error_32_sunplex.gif";
655: }
656: return image;
657: }
658:
659: /**
660: * gets content style of message box
661: *
662: * @param type of message box
663: * @return content style of message box
664: */
665: protected String getContentStyle(int type) {
666: String style = null;
667:
668: if (type == TYPE_WARNING) {
669: style = "alert-warning-content";
670: } else if (type == TYPE_INFORMATION) {
671: style = "alert-info-content";
672: } else {
673: style = "alert-error-content";
674: }
675: return style;
676: }
677:
678: /**
679: * gets border style of message box
680: *
681: * @param type of message box
682: * @return border style of message box
683: */
684: protected String getBorderStyle(int type) {
685: String style = null;
686:
687: if (type == TYPE_WARNING) {
688: style = "alert-warning-frame";
689: } else if (type == TYPE_INFORMATION) {
690: style = "alert-info-frame";
691: } else {
692: style = "alert-error-frame";
693: }
694: return style;
695: }
696:
697: }
|