Index: src/java/org/apache/solr/core/SolrCore.java
===================================================================
--- src/java/org/apache/solr/core/SolrCore.java	(revision 607112)
+++ src/java/org/apache/solr/core/SolrCore.java	(working copy)
@@ -54,6 +54,7 @@
 import org.apache.solr.request.JSONResponseWriter;
 import org.apache.solr.request.PythonResponseWriter;
 import org.apache.solr.request.QueryResponseWriter;
+import org.apache.solr.request.RawResponseWriter;
 import org.apache.solr.request.RubyResponseWriter;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrQueryResponse;
@@ -960,7 +961,9 @@
     if (responseWriters.get("ruby")==null) {
       responseWriters.put("ruby", new RubyResponseWriter());
     }
-
+    if (responseWriters.get("raw")==null) {
+      responseWriters.put("raw", new RawResponseWriter());
+    }
   }
   
   /** Finds a writer by name, or returns the default writer if not found. */
Index: src/java/org/apache/solr/request/RawResponseWriter.java
===================================================================
--- src/java/org/apache/solr/request/RawResponseWriter.java	(revision 0)
+++ src/java/org/apache/solr/request/RawResponseWriter.java	(revision 0)
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * 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.
+ */
+
+package org.apache.solr.request;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * Writes a ContentStream directly to the output.
+ *
+ * <p>
+ * This writer is a special case that extends and alters the
+ * QueryResponseWriter contract.  If SolrQueryResponse contains a
+ * ContentStream added with the key {@link #CONTENT}
+ * then this writer will output that stream exactly as is (with it's
+ * Content-Type).  if no such ContentStream has been added, then a
+ * "base" QueryResponseWriter will be used to write the response
+ * according to the usual contract.  The name of the "base" writer can
+ * be specified as an initialization param for this writer, or it
+ * defaults to the "standard" writer.
+ * </p>s
+ */
+public class RawResponseWriter implements QueryResponseWriter 
+{
+  /** 
+   * The key that should be used to add a ContentStream to the 
+   * SolrQueryResponse if you intend to use this Writer.
+   */
+  public static final String CONTENT = "content";
+  private String _baseWriter = null;
+  
+  public void init(NamedList n) {
+    if( n != null ) {
+      Object base = n.get( "base" );
+      if( base != null ) {
+        _baseWriter = base.toString();
+      }
+    }
+  }
+
+  // Even if this is null, it should be ok
+  protected QueryResponseWriter getBaseWriter( SolrQueryRequest request )
+  {
+    return request.getCore().getQueryResponseWriter( _baseWriter );
+  }
+  
+  public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
+    Object obj = response.getValues().get( CONTENT );
+    if( obj != null && (obj instanceof ContentStream ) ) {
+      // copy the contents to the writer...
+      ContentStream content = (ContentStream)obj;
+      return content.getContentType();
+    }
+    return getBaseWriter( request ).getContentType( request, response );
+  }
+
+  public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException 
+  {
+    Object obj = response.getValues().get( CONTENT );
+    if( obj != null && (obj instanceof ContentStream ) ) {
+      // copy the contents to the writer...
+      ContentStream content = (ContentStream)obj;
+      IOUtils.copy( content.getStream(), writer );
+    }
+    else {
+      getBaseWriter( request ).write( writer, request, response );
+    }
+  }
+}

Property changes on: src\java\org\apache\solr\request\RawResponseWriter.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java
===================================================================
--- src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java	(revision 0)
+++ src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java	(revision 0)
@@ -0,0 +1,234 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * 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.
+ */
+
+package org.apache.solr.handler.admin;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.handler.RequestHandlerBase;
+import org.apache.solr.handler.RequestHandlerUtils;
+import org.apache.solr.request.RawResponseWriter;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrQueryResponse;
+
+/**
+ * This handler uses the RawResponseWriter to give client access to
+ * files inside ${solr.home}/conf
+ * 
+ * If you want to selectively restrict access some configuration files, you can list
+ * these files in the {@link #HIDDEN} invariants.  For example to hide 
+ * synonyms.txt and anotherfile.txt, you would register:
+ * 
+ * <pre>
+ * &lt;requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" &gt;
+ *   &lt;lst name="defaults"&gt;
+ *    &lt;str name="echoParams"&gt;explicit&lt;/str&gt;
+ *    &lt;str name="wt"&gt;raw&lt;/str&gt; &lt;!-- spit back a raw ContentStream --&gt;
+ *   &lt;/lst&gt;
+ *   &lt;lst name="invariants"&gt;
+ *    &lt;str name="hidden"&gt;synonyms.txt&lt;/str&gt; 
+ *    &lt;str name="hidden"&gt;anotherfile.txt&lt;/str&gt; 
+ *   &lt;/lst&gt;
+ * &lt;/requestHandler&gt;
+ * </pre>
+ * 
+ * If you want to override the contentType header returned for a given file, you can
+ * set it directly using: {@link #USE_CONTENT_TYPE}.  For example, to get a plain text 
+ * version of schema.xml, try:
+ * <pre>
+ *   http://localhost:8983/solr/admin/file?file=schema.xml&contentType=text/plain
+ * </pre>
+ */
+public class ShowFileRequestHandler extends RequestHandlerBase
+{
+  public static final String HIDDEN = "hidden";
+  public static final String USE_CONTENT_TYPE = "contentType";
+  
+  protected Set<String> hiddenFiles;
+  
+  private static ShowFileRequestHandler instance;
+  public ShowFileRequestHandler()
+  {
+    super();
+    instance = this; // used so that getFileContents can access hiddenFiles
+  }
+
+  @Override
+  public void init(NamedList args) {
+    super.init( args );
+    
+    hiddenFiles = new HashSet<String>();
+    if( invariants != null ) {
+      String[] hidden = invariants.getParams( HIDDEN );
+      if( hidden != null ) {
+        for( String s : hidden ) {
+          hiddenFiles.add( s.toUpperCase() );
+        }
+      }
+    }
+  }
+  
+  @Override
+  public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException 
+  {
+    File adminFile = null;
+    
+    final SolrResourceLoader loader = req.getCore().getResourceLoader();
+    File configdir = new File( loader.getConfigDir() ); 
+    String fname = req.getParams().get("file", null);
+    if( fname == null ) {
+      adminFile = configdir;
+    }
+    else {
+      fname = fname.replace( '\\', '/' ); // normalize slashes
+      if( hiddenFiles.contains( fname.toUpperCase() ) ) {
+        throw new SolrException( ErrorCode.FORBIDDEN, "Can not access: "+fname );
+      }
+      if( fname.indexOf( ".." ) >= 0 ) {
+        throw new SolrException( ErrorCode.FORBIDDEN, "Invalid path: "+fname );  
+      }
+      adminFile = new File( configdir, fname );
+    }
+    
+    // Make sure the file exists, is readable and is not a hidden file
+    if( !adminFile.exists() || !adminFile.canRead() || adminFile.isHidden() ) {
+      throw new SolrException( ErrorCode.BAD_REQUEST, "Can not find: "+adminFile.getName() );
+    }
+    
+    // Add a warning
+    RequestHandlerUtils.addExperimentalFormatWarning(rsp);
+    
+    // Show a directory listing
+    if( adminFile.isDirectory() ) {
+      
+      int basePath = configdir.getAbsolutePath().length() + 1;
+      NamedList<SimpleOrderedMap<Object>> files = new SimpleOrderedMap<SimpleOrderedMap<Object>>();
+      for( File f : adminFile.listFiles() ) {
+        String path = f.getAbsolutePath().substring( basePath );
+        path = path.replace( '\\', '/' ); // normalize slashes
+        if( hiddenFiles.contains( path.toUpperCase() ) ) {
+          continue; // don't show 'hidden' files
+        }
+        if( f.isHidden() || f.getName().startsWith( "." ) ) {
+          continue; // skip hidden system files...
+        }
+        
+        SimpleOrderedMap<Object> fileInfo = new SimpleOrderedMap<Object>();
+        files.add( path, fileInfo );
+        if( f.isDirectory() ) {
+          fileInfo.add( "directory", true ); 
+        }
+        else {
+          // TODO? content type
+          fileInfo.add( "size", f.length() );
+        }
+        fileInfo.add( "modified", new Date( f.lastModified() ) );
+      }
+      rsp.add( "files", files );
+    }
+    else {
+      // Check if they want the file as text
+      final String contentType = req.getParams().get( USE_CONTENT_TYPE );
+      
+      final File file = adminFile;
+      //final URLConnection conn = adminFile.toURI().toURL().openConnection();
+      
+      ContentStream stream = new ContentStream() {
+        public String getName() { return file.getName(); }
+        public Long getSize() { return file.length(); }
+        public String getSourceInfo() { return null; }
+
+        public String getContentType() { 
+          if( contentType != null ) {
+            return contentType;
+          }
+          return null; //conn.getContentType(); 
+        }
+        
+        public InputStream getStream() throws IOException {
+          return loader.openResource( file.getPath() ); //conn.getInputStream();
+        }
+        
+        public Reader getReader() throws IOException {
+          return new FileReader( file );
+        }
+      };
+      
+      rsp.add( RawResponseWriter.CONTENT, stream );
+    }
+  }
+  
+  /**
+   * This is a utility function that lets you get the contents of an admin file
+   * 
+   * It is only used so that we can get rid of "/admin/get-file.jsp" and include
+   * "admin-extra.html" in "/admin/index.html" using jsp scriptlets
+   */
+  @Deprecated
+  public static String getFileContents( String path )
+  {
+    if( instance != null && instance.hiddenFiles != null ) {
+      if( instance.hiddenFiles.contains( path ) ) {
+        return ""; // ignore it...
+      }
+    }
+    try {
+      SolrCore core = SolrCore.getSolrCore();
+      InputStream input = core.getResourceLoader().openResource(path);
+      return IOUtils.toString( input );
+    }
+    catch( Exception ex ) {} // ignore it
+    return "";
+  }
+
+  //////////////////////// SolrInfoMBeans methods //////////////////////
+
+  @Override
+  public String getDescription() {
+    return "Admin Get File -- view config files directly";
+  }
+
+  @Override
+  public String getVersion() {
+      return "$Revision$";
+  }
+
+  @Override
+  public String getSourceId() {
+    return "$Id$";
+  }
+
+  @Override
+  public String getSource() {
+    return "$URL$";
+  }
+}

Property changes on: src\java\org\apache\solr\handler\admin\ShowFileRequestHandler.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: src/webapp/resources/admin/get-file.jsp
===================================================================
--- src/webapp/resources/admin/get-file.jsp	(revision 607112)
+++ src/webapp/resources/admin/get-file.jsp	(working copy)
@@ -23,6 +23,13 @@
                  java.io.Reader,
                  java.util.StringTokenizer"%>
 <%
+//
+// NOTE -- this file is Deprecated - should use the ShowFileRequestHandler instead
+//
+
+System.out.println( "WARNING -- using deprecated jsp file: " + request.getServletPath() );
+
+
   Object ocore = request.getAttribute("org.apache.solr.SolrCore");
   SolrCore core = ocore instanceof SolrCore? (SolrCore) ocore : SolrCore.getSolrCore();
   String fname = request.getParameter("file");
Index: src/webapp/resources/admin/index.jsp
===================================================================
--- src/webapp/resources/admin/index.jsp	(revision 607112)
+++ src/webapp/resources/admin/index.jsp	(working copy)
@@ -34,8 +34,8 @@
 	<h3>Solr</h3>
   </td>
   <td>
-    [<a href="get-file.jsp?file=<%=core.getSchemaFile()%>">Schema</a>]
-    [<a href="get-file.jsp?file=<%=core.getConfigFile()%>">Config</a>]
+    [<a href="file/?file=<%=core.getSchemaFile()%>">Schema</a>]
+    [<a href="file/?file=<%=core.getConfigFile()%>">Config</a>]
     [<a href="analysis.jsp?highlight=on">Analysis</a>]
     <br>
     [<a href="stats.jsp">Statistics</a>]
@@ -80,7 +80,10 @@
 </tr>
 
 
-<jsp:include page="get-file.jsp?file=admin-extra.html&optional=y" flush="true"/>
+<%
+ // a quick hack to get rid of get-file.jsp -- note this still spits out invalid HTML
+ out.write( org.apache.solr.handler.admin.GetAdminFileRequestHandler.getFileContents( "admin-extra.html" ) );
+%>
 
 </table><P>
 
Index: example/solr/conf/solrconfig.xml
===================================================================
--- example/solr/conf/solrconfig.xml	(revision 607112)
+++ example/solr/conf/solrconfig.xml	(working copy)
@@ -500,6 +500,20 @@
   <requestHandler name="/admin/threads"    class="org.apache.solr.handler.admin.ThreadDumpHandler" />
   <requestHandler name="/admin/properties" class="org.apache.solr.handler.admin.PropertiesRequestHandler" />
   
+  <requestHandler name="/admin/file" class="org.apache.solr.handler.admin.ShowFileRequestHandler" >
+    <lst name="defaults">
+     <!-- If you change this, the handler will not work as expected -->
+     <str name="wt">raw</str> 
+    </lst>
+    <!--
+     You can selectivly "hide" some files under ${solr.home}/conf
+    <lst name="invariants">
+     <str name="hidden">synonyms.txt</str> 
+     <str name="hidden">anotherfile.txt</str> 
+    </lst>
+    -->
+  </requestHandler>
+   
   <!-- ping/healthcheck -->
   <requestHandler name="/admin/ping" class="PingRequestHandler">
     <lst name="defaults">
@@ -564,7 +578,17 @@
 
     <queryResponseWriter name="custom" class="com.example.MyResponseWriter"/>
   -->
+  
+  <!--
+    The "raw" writer is also available by default, but is a very
+    special case.  It allows Request Handlers to specify a raw
+    ContentStream to be written back to as the response.
 
+    It is recommended that this not be overridden.
+
+    <queryResponseWriter name="raw" class="org.apache.solr.request.RawResponseWriter"/>
+  -->
+
   <!-- XSLT response writer transforms the XML output by any xslt file found
        in Solr's conf/xslt directory.  Changes to xslt files are checked for
        every xsltCacheLifetimeSeconds.  
