/* * Copyright (C) 2000 - 2012 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://openbd.org/ * $Id: cfmRunTimeException.java 2326 2013-02-09 19:30:40Z alan $ */ package com.naryx.tagfusion.cfm.engine; import java.io.BufferedReader; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import com.naryx.tagfusion.cfm.file.cfFile; import com.naryx.tagfusion.cfm.file.cfmlFileCache; import com.naryx.tagfusion.cfm.file.cfmlURI; import com.naryx.tagfusion.cfm.file.sourceReader; import com.naryx.tagfusion.cfm.tag.cfDUMP; import com.naryx.tagfusion.cfm.tag.cfERROR; import com.naryx.tagfusion.cfm.tag.cfOUTPUT; import com.naryx.tagfusion.cfm.tag.cfSCRIPT; import com.naryx.tagfusion.cfm.tag.cfTRY; import com.naryx.tagfusion.expression.compile.expressionEngine; public class cfmRunTimeException extends Exception { private static final long serialVersionUID = 1L; protected cfCatchData catchData; private Throwable incomingException; private String output; //this var and its use is part of the fix for NA bug #3282 private boolean rethrow = false; private java.io.File logFile = null; public void setOutput( String o ) { output = o; } public String getOutput() { return ( output == null ? "" : output ); } public boolean isRethrow() { return rethrow; } public cfmRunTimeException() { super(); catchData = new cfCatchData(); } public cfmRunTimeException(cfSession session, Throwable E) { super(); catchData = new cfCatchData(session); catchData.setInternal(true); incomingException = E; } public cfmRunTimeException(cfCatchData _catchData) { this(_catchData, false); } public cfmRunTimeException(cfCatchData _catchData, boolean _rethrow) { super(_catchData.getString("message")); catchData = _catchData; rethrow = _rethrow; } public cfmRunTimeException(cfCatchData _catchData, cfmBadFileException badFile) { catchData = _catchData; this.incomingException = badFile; } /** * Overrides java.lang.Throwable. We don't care about Java stack traces for * CFML runtime exceptions, so save the effort of filling it in. */ public Throwable fillInStackTrace() { return this; } public java.io.File getLogFile() { return logFile; } public String getMessage() { cfCatchData data; // If there are multiple errors then retrieve the message for the first error List<cfCatchData> errors = catchData.getErrorList(); if (errors == null) data = catchData; else data = errors.get(0); String detail = data.getString("detail"); if ((detail != null) && (detail.length() > 0)) return detail; return data.getString("message"); } public String getMessageThenDetail() { cfCatchData data; // If there are multiple errors then retrieve the message for the first error List<cfCatchData> errors = catchData.getErrorList(); if (errors == null) data = catchData; else data = errors.get(0); String message = data.getString("message"); if ((message != null) && (message.length() > 0)) return message; return data.getString("detail"); } public cfCatchData getCatchData() { return catchData; } public String toString() { return catchData.toString(); } public void handleException(cfSession _Session) { try { _Session.setData(cfTRY.CFCATCHVAR, catchData); } catch (cfmRunTimeException e) { // shouldn't occur cfEngine.log("Unexpected exception in cfmRunTimeException.handleException: "); } catchData.setSession(_Session); _Session.recordException(this, _Session.activeFile(), _Session.activeTag()); _Session.clearCfSettings(); _Session.setSuppressWhiteSpace(false); // Determine a logfile if they want to have all RunTimeExceptions saved if (cfEngine.thisPlatform.getFileIO().isRunTimeLoggingEnabled()) { logFile = cfEngine.thisPlatform.getFileIO().getRunTimeLoggingFile(); if (logFile != null) { // make sure log file was created successfully catchData.setData("errorlogfile", new cfStringData(logFile.getAbsolutePath())); } } // Check to see if the user has specified a CFERROR tag for this error boolean errorHandled = false; cfErrorData errorData = (cfErrorData) _Session.getDataBin(cfERROR.DATA_BIN_KEY); if (errorData != null) { if (errorData.handleExceptionError(_Session, catchData) || errorData.handleRequestError(_Session, catchData)) { errorHandled = true; if (logFile == null) return; // not logging the error } } if (!errorHandled) { errorHandled = runDefaultErrorHandler(_Session); } if (!errorHandled) { try { // set the response status code to 500 before flushing the page _Session.setStatus(javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } catch (cfmRunTimeException ignore) {} // flush the output so you can see how far the output got prior to the // exception _Session.pageFlush(); } StringBuilder buffer = new StringBuilder(32000); boolean internalError = catchData.isInternal(); buffer.append("\r\n<p></p><html><title>OpenBD:"); buffer.append( (internalError ? "Internal Server Error" : "CFML RunTime Error") + "</title><body>"); String fontSize = (_Session.isWindowsOrMacUser() ? "11px" : "13px"); buffer.append("<style>TD {font-family : Verdana, Geneva, Arial, Helvetica, sans-serif; font-size : " + fontSize + "; }"); buffer.append(".redheader { background-color: #CC0033;background-image: -webkit-gradient(linear, left top, left bottom, from(#CC0033), to(#ff0000)); background-image: -webkit-linear-gradient(top,#CC0033,#ff0000); background-image:-moz-linear-gradient(top, #CC0033, #ff0000); background-image: -ms-linear-gradient(top, #CC0033, #ff0000); background-image: -o-linear-gradient(top, #CC0033, #ff0000); background-image: linear-gradient(top, #CC0033, #ff0000);filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#CC0033', EndColorStr='#ff0000'); }.header_table { width: 100%; background: #fff; border-collapse: collapse; border-spacing: 0; border: 1px solid #444; -webkit-box-shadow: 0px 1px 3px #888; -moz-box-shadow: 0px 1px 3px #888; box-shadow: 0px 1px 3px #888; }.header_table tr { border-top: 1px solid #444; }</style>"); buffer.append("<table class='header_table' width='100%' border='1' cellspacing='0' cellpadding='5' bgcolor='white'" + (internalError ? " BORDERCOLOR='Black'" : "") + ">"); if (internalError) { buffer.append("<tr bgcolor='#ffff00'><td colspan='2' align='left'><a href='http://openbd.org/manual/'><img style='float: right; margin: .20em .1em .1em .3em;' src='"); if ( cfEngine.OpenBDLogoDataUri != null ) buffer.append( cfEngine.OpenBDLogoDataUri ); else buffer.append( "" ); buffer.append( "'/></a><font size='+1' color='black'><b>OpenBD Internal Server Error</b></font><div><a style='text-decoration:none;color:grey;margin-left:20' href='http://openbd.org/manual/'>Need some help? Visit our manual http://openbd.org/manual/</a></div</td></tr><tr><td colspan=2><b>The page you were executing caused an internal server error</b></td></tr>"); } else { buffer.append("<tr class='redheader' bgcolor='#CC0033'><td colspan='2' align='left'><a href='http://openbd.org/manual/'><img style='float: right; margin: .20em .1em .1em .3em;' src='"); if ( cfEngine.OpenBDLogoDataUri != null ) buffer.append( cfEngine.OpenBDLogoDataUri ); else buffer.append( "" ); buffer.append( "'/></a><font size='+1' color='yellow'><b>CFML Runtime Error</b></font><div><a style='text-decoration:none;color:white;margin-left:20' href='http://openbd.org/manual/'>Need some help? Visit our manual http://openbd.org/manual/</a></div</td></tr>"); if (catchData.containsKey("message")) { buffer.append("<tr><td colspan=2><b>"); String s = com.nary.util.string.escapeHtml(catchData.getString("message")); if ( s.startsWith("Function: ") ){ int c1 = s.indexOf(":")+2; int c2 = s.indexOf("(", c1); if ( c1 != -1 && c2 != 1 ){ String fn = s.substring(c1,c2); s = s.substring( 0, c1 ) + "<a style='text-decoration:none;color:blue;' target='_blank' title='click here for documentation' href='http://openbd.org/manual/?/function/" + fn + "'>" + fn + "</a>" + s.substring(c2); } } buffer.append(s); buffer.append("</b></td></tr>"); } } StringBuilder extendedBuffer = new StringBuilder( 64000 ); // for debug and logging info if (cfEngine.isDebugOutputEnabled() || (logFile != null)) { // Print the Request that made this extendedBuffer.append("<tr><td width='1%' nowrap>Request<td>"); extendedBuffer.append(com.nary.util.string.escapeHtml(_Session.getRequestURI())); extendedBuffer.append("</td></tr><tr><td width='1%' valign='top' nowrap>File Trace<td>"); extendedBuffer.append(fileStack(_Session)); extendedBuffer.append("</td></tr></table><p><table width='100%' border='0' cellspacing='1' cellpadding='5' bgcolor='f4f4f4'>"); if (internalError) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Type</td><td>Internal</td></tr>"); } else if (catchData.containsKey("type")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Type</td><td>"); extendedBuffer.append(catchData.getString("type")); extendedBuffer.append("</td></tr>"); } if ( catchData.containsKey("errnumber") ){ String err = catchData.getString("errnumber"); if ( err.indexOf("function") != -1 ){ String func = expressionEngine.getFunctionName( err ); if ( func != null ){ extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Function</td><td><a style='text-decoration:none;color:blue;' target='_blank' title='click here for documentation' href='http://openbd.org/manual/?/function/"); extendedBuffer.append( func ); extendedBuffer.append( "'>" ); extendedBuffer.append( func ); extendedBuffer.append("</a></td></tr>"); } } } String[] functionList = catchData.getFunctionList(); if (functionList.length > 0) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>Function(s)</td><td>"); extendedBuffer.append(functionList(functionList)); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("queryerror")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Query Error</td><td>"); extendedBuffer.append(com.nary.util.string.escapeHtml(catchData.getString("queryerror"))); extendedBuffer.append("</td></tr>"); if (catchData.containsKey("datasource")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Datasource</td><td>"); extendedBuffer.append(catchData.getString("datasource")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("nativeerrorcode")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Native Error Code</td><td>"); extendedBuffer.append(catchData.getString("nativeerrorcode")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("sqlstate")) { extendedBuffer.append("<TR BGCOLOR='E0E0E0'><TD WIDTH='1%' NOWRAP>SQL State</td><td>"); extendedBuffer.append(catchData.getString("sqlstate")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("sql")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>SQL</td><td>"); extendedBuffer.append(com.nary.util.string.escapeHtml(catchData.getString("sql"))); extendedBuffer.append("</td></tr>"); } } else { if (catchData.containsKey("detail")) { String detail = catchData.getString("detail"); if (detail.length() > 0) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>Detail</td><td>"); extendedBuffer.append(com.nary.util.string.escapeHtml(detail)); extendedBuffer.append("</td></tr>"); } } if (catchData.containsKey("extendedinfo")) { String extendedInfo = catchData.getString("extendedinfo"); if (extendedInfo.length() > 0) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>Extended Info</td><td>"); extendedBuffer.append(com.nary.util.string.escapeHtml(extendedInfo)); extendedBuffer.append("</td></tr>"); } } } if (catchData.containsKey("missingfilename")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Missing File</td><td>"); extendedBuffer.append(catchData.getString("missingfilename")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("lockname")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' nowrap>Lock Name</td><td>"); extendedBuffer.append(catchData.getString("lockname")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("lockoperation")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%'>Lock Operation</td><td>"); extendedBuffer.append(catchData.getString("lockoperation")); extendedBuffer.append("</td></tr>"); } if (catchData.containsKey("tagcontext")) { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>Tag Context</td><td>"); extendedBuffer.append(tagStack((cfArrayData) catchData.getData("tagcontext"))); extendedBuffer.append("</td></tr>"); } // Get source, but not for precompiled templates // activeFile may be null if Exception occurs after the last popActive() cfFile activeFile = _Session.activeFile(); if (activeFile != null) { printSourceCode( _Session, extendedBuffer, catchData, activeFile.getCfmlURI() ); } // If there is any suberrors List<cfCatchData> listError = catchData.getErrorList(); if (listError != null) { extendedBuffer.append("<tr><td bgcolor='white'> </td><td>"); printSubErrors(_Session, extendedBuffer, listError.iterator()); extendedBuffer.append("</td></tr>"); } // Display the Stack Trace if (incomingException != null) { if (incomingException instanceof cfmBadFileException) { cfmBadFileException bfe = (cfmBadFileException) incomingException; if (!bfe.fileNotFound()) { cfCatchData bfeCatchData = bfe.getCatchData(); listError = bfeCatchData.getErrorList(); if (listError == null) { listError = new ArrayList<cfCatchData>(); listError.add(bfeCatchData); } printSubErrors(_Session, extendedBuffer, listError.iterator()); } } else { extendedBuffer.append("<tr bgcolor='e0e0e0'><td width='1%' valign='top' nowrap>Stack Trace</td><td><pre>"); extendedBuffer.append(com.nary.Debug.getStackTraceAsString(incomingException)); extendedBuffer.append("</pre></td></tr>"); } } extendedBuffer.append("</table><br/>\r\n\r\n"); // Get all the DUMP information try { extendedBuffer.append(cfDUMP.dumpSession(_Session)); // LONG dump of all variables } catch (cfmRunTimeException ignore) { } catch (Throwable t) { cfEngine.log("-] ERROR: caught exception when dumping session data - " + t.toString()); } if (logFile != null) { cfEngine.thisPlatform.getFileIO().writeLogFile(logFile, buffer.toString() + extendedBuffer.toString() + "\r\n\r\n<p></body></html>\r\n"); } if (cfEngine.isDebugOutputEnabled()) buffer.append(extendedBuffer.toString()); if (logFile != null) { buffer.append("<p><table><tr><td>This error has been logged to " + logFile.getName() + ".</td><td> Please contact the Administrator</td></tr></table>"); } } buffer.append("\r\n\r\n<p></body></html>\r\n"); if (!errorHandled) { // Write it out to the page _Session.write(buffer.toString()); _Session.pageEnd(); } } protected boolean runDefaultErrorHandler(cfSession _Session) { try { String defErrHandler = cfEngine.getConfig().getString("server.system.errorhandler", ""); if (!defErrHandler.equals("")) { cfErrorData.setSession(_Session, catchData); cfFile handler = _Session.getFile(new cfmlURI(defErrHandler)); _Session.reset(); _Session.setContentType("text/html"); _Session.pushActiveFile(handler); _Session.write(handler.renderToString(_Session).getOutput()); _Session.pageEnd(); return true; } } catch (cfmRunTimeException e) { cfEngine.log("Error rendering default error handler"); } return false; } protected String tagStack(cfArrayData tagArray) { try { StringBuilder buffer = new StringBuilder(32); int tab = 0; int arraySize = tagArray.size(); for (int x = 0; x < arraySize; x++) { if (tab > 0) { buffer.append(space(tab - 4) + "|<br/>"); buffer.append(space(tab - 4) + "+-- "); } cfStructData SD = (cfStructData) tagArray.getElement(x + 1); buffer.append( "<a style='text-decoration:none;color:blue;' target='_blank' title='click here for documentation' href='http://openbd.org/manual/?/tag/" + SD.getData("id").getString() + "'>" ); buffer.append( SD.getData("id").getString() ); buffer.append( "</a> (" + SD.getData( cfCatchData.TEMPLATE_KEY ) + ", Line=" + SD.getData( cfCatchData.LINE_KEY ).getInt() + ", Column=" + SD.getData( cfCatchData.COLUMN_KEY ).getInt() + ")" ); buffer.append("<br/>"); tab += 4; } String buf = buffer.toString(); if (buf.length() > 4) return buf.substring(0, buf.length() - 5); else return ""; } catch (Exception ignoreException) { return ""; } } private String functionList(String[] functionList) { StringBuilder sb = new StringBuilder( 128 ); int tab = 0; for (int i = 0; i < functionList.length; i++) { if (tab > 0) { sb.append(space(tab - 4) + "|<br/>"); sb.append(space(tab - 4) + "+--"); } sb.append(functionList[i]); sb.append("<br/>"); tab += 4; } return sb.toString(); } private String fileStack(cfSession _Session) { try { Enumeration<cfFile> E = _Session.fileStackEnumeration(); StringBuilder buffer = new StringBuilder(32); int tab = 0; while (E.hasMoreElements()) { if (tab > 0) { buffer.append(space(tab - 4) + "|<br/>"); buffer.append(space(tab - 4) + "+-- "); } buffer.append(E.nextElement().getName()); buffer.append("<br/>"); tab += 4; } String buf = buffer.toString(); return buf.substring(0, buf.length() - 5); } catch (Exception ignoreException) { return ""; } } private static String space(int x) { StringBuilder tmp = new StringBuilder( 16 ); for (int q = 0; q < x; q++) tmp.append( " " ); return tmp.toString(); } private static String padNumber(int x, int pad) { String tmp = x + ""; int t = pad - tmp.length(); for (int v = 0; v < t; v++) tmp += " "; return tmp; } // ---------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------- protected void printSubErrors(cfSession _Session, StringBuilder buffer, Iterator<cfCatchData> i) { while (i.hasNext()) { cfCatchData cd = i.next(); if (cd.containsKey("type")) { buffer.append("<TR BGCOLOR='E0E0E0'><TD WIDTH='1%' NOWRAP>Type</TD><TD>"); buffer.append(cd.getString("type")); buffer.append("</TD></TR>"); } buffer.append("<TR BGCOLOR='E0E0E0'><TD WIDTH='1%' NOWRAP>Message</TD><TD>"); buffer.append(cd.getString("message")); buffer.append("</TD></TR>"); if (cd.getString("tagname").length() > 0) { buffer.append("<TR BGCOLOR='E0E0E0'><TD WIDTH='1%' NOWRAP>Tag</TD><TD>"); buffer.append(com.nary.util.string.escapeHtml(cd.getString("tagname"))); buffer.append("</TD></TR>"); } if (cd.getString("line").length() > 0) { buffer.append("<TR BGCOLOR='E0E0E0' WIDTH='1%' NOWRAP><TD>Position</TD><TD>Line="); buffer.append(cd.getString("line")); buffer.append("; Column="); buffer.append(cd.getString("column")); buffer.append("</TD></TR>"); } if (cd.getString("detail").length() > 0) { buffer.append("<TR BGCOLOR='E0E0E0' WIDTH='1%' NOWRAP><TD>Detail</TD><TD>"); buffer.append(com.nary.util.string.escapeHtml(cd.getString("detail"))); buffer.append("</TD></TR>"); } if (cd.getString("extendedinfo").length() > 0) { buffer.append("<TR BGCOLOR='E0E0E0' WIDTH='1%' NOWRAP><TD>Extended Info</TD><TD>"); buffer.append(com.nary.util.string.escapeHtml(cd.getString("extendedinfo"))); buffer.append("</TD></TR>"); } printSourceCode(_Session, buffer, cd, null); } } // ---------------------------------------------------------------------------------- private void printSourceCode(cfSession _Session, StringBuilder buffer, cfCatchData cd, cfmlURI uri) { cfmlURI thisUrl = (uri == null) ? cd.getFileURI() : uri; if (thisUrl == null) return; // If a line wasn't specified then we can't display the source code cfData lineErrorData = cd.getData("line"); if (lineErrorData == null) return; try { BufferedReader in = cfmlFileCache.getReader(_Session.REQ, _Session.CTX, thisUrl); int lineError = lineErrorData.getInt(); cfData scriptLine = cd.getData("scriptline"); if ( scriptLine != null && ( (_Session.getTagStack().empty() && this instanceof cfmBadFileException) || (_Session.getTagStack().peek() instanceof cfSCRIPT || _Session.getTagStack().peek() instanceof cfOUTPUT) )) { lineError += scriptLine.getInt(); } sourceReader sR = new sourceReader(in, lineError - 4, 5); String[] lines = sR.getLines(); int padSize = String.valueOf(lineError+5).length(); buffer.append("<tr bgcolor='E0E0E0'><td width='1%' valign='top' nowrap>Source</td><td><pre>"); for (int x = 0; x < lines.length; x++) { if (lines[x] == null) break; if (lineError == ((x + sR.getLineStart() + 1))) buffer.append("<font color='red'>"); buffer.append(padNumber((x + sR.getLineStart() + 1), padSize)); buffer.append(": "); buffer.append(com.nary.util.string.escapeHtml(com.nary.util.string.replaceString(lines[x], "\t", " "))); if (lineError == ((x + sR.getLineStart() + 1))) buffer.append("</font>"); if (x < lines.length - 1) buffer.append("\r\n"); } buffer.append("</pre>^ Snippet from underlying CFML source</td></tr>"); in.close(); } catch (Exception E) { cfEngine.log("Error: " + E.toString() + ", displaying source code snippet for: " + thisUrl.getRealPath(_Session.REQ)); } } }