Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

471 lines (409 sloc) 14.092 kb
<%@ WebHandler Language="c#" class="Mono.Website.Handlers.MonodocHandler" %>
<%@ Assembly name="monodoc" %>
#define MONODOC_PTREE
//
// Mono.Web.Handlers.MonodocHandler.
//
// Authors:
// Ben Maurer (bmaurer@users.sourceforge.net)
// Miguel de Icaza (miguel@novell.com)
//
// (C) 2003 Ben Maurer
// (C) 2006 Novell, Inc.
//
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Xml;
using System.Xml.Xsl;
using System.Linq;
using Monodoc;
using System.Text.RegularExpressions;
namespace Mono.Website.Handlers
{
public class MonodocHandler : IHttpHandler
{
static DateTime monodoc_timestamp, handler_timestamp;
static MonodocHandler ()
{
monodoc_timestamp = File.GetCreationTimeUtc (typeof (Node).Assembly.Location);
handler_timestamp = File.GetCreationTimeUtc (typeof (MonodocHandler).Assembly.Location);
DumpEmbeddedImages ();
}
// Dumps the embedded images from monodoc.dll
static void DumpEmbeddedImages ()
{
try {
Directory.CreateDirectory ("mdocimages");
} catch {}
var mass = typeof (Node).Assembly;
var buffer = new byte [4096];
foreach (string image in mass.GetManifestResourceNames ()){
if (!(image.EndsWith ("png") || image.EndsWith ("jpg")))
continue;
var target = Path.Combine ("mdocimages", image);
if (File.Exists (target))
continue;
using (var output = File.Create (target)){
var input = mass.GetManifestResourceStream (image);
int n;
while ((n = input.Read (buffer, 0, buffer.Length)) > 0){
output.Write (buffer, 0, n);
}
}
}
}
void IHttpHandler.ProcessRequest (HttpContext context)
{
string s;
s = (string) context.Request.Params["link"];
if (s != null){
HandleMonodocUrl (context, s);
return;
}
s = (string) context.Request.Params["tree"];
Console.WriteLine ("tree request: '{0}'", s);
if (s != null){
HandleTree (context, s);
return;
}
s = (string) context.Request.Params["fsearch"];
Console.WriteLine ("Fast search requested for query {0}", s);
if (s != null) {
HandleFastSearchRequest (context, s);
return;
}
s = (string) context.Request.Params["search"];
Console.WriteLine ("Full search requested for query {0}", s);
if (s != null) {
HandleFullSearchRequest (context, s);
return;
}
context.Response.Write ("<html><body>Unknown request</body></html>");
context.Response.ContentType = "text/html";
}
void HandleTree (HttpContext context, string tree)
{
context.Response.ContentType = "text/xml";
//Console.WriteLine ("Tree request: " + tree);
try {
//
// Walk the url, found what we are supposed to render.
//
string [] nodes = tree.Split (new char [] {'@'});
Node current_node = Global.help_tree;
for (int i = 0; i < nodes.Length; i++){
try {
current_node = (Node)current_node.Nodes [int.Parse (nodes [i])];
} catch (Exception e){
Console.WriteLine ("Failure with: {0} {1}", tree, i);
}
}
XmlTextWriter w = new XmlTextWriter (context.Response.Output);
w.WriteStartElement ("tree");
for (int i = 0; i < current_node.Nodes.Count; i++) {
Node n = (Node)current_node.Nodes [i];
w.WriteStartElement ("tree");
w.WriteAttributeString ("text", n.Caption);
if (n.tree != null && n.tree.HelpSource != null)
w.WriteAttributeString ("action", HttpUtility.UrlEncode (n.PublicUrl));
if (n.Nodes != null){
w.WriteAttributeString ("src", tree + "@" + i);
}
w.WriteEndElement ();
}
w.WriteEndElement ();
} catch (Exception e) {
Console.WriteLine (e);
}
//Console.WriteLine ("Tree request satisfied");
}
void CheckLastModified (HttpContext context)
{
string strHeader = context.Request.Headers ["If-Modified-Since"];
DateTime lastHelpSourceTime = Global.help_tree.LastHelpSourceTime;
try {
if (strHeader != null && lastHelpSourceTime != DateTime.MinValue) {
// Console.WriteLine ("Got this: {0}", strHeader);
DateTime dtIfModifiedSince = DateTime.ParseExact (strHeader, "r", null).ToUniversalTime ();
DateTime ftime = lastHelpSourceTime.ToUniversalTime ();
//Console.WriteLine ("Times:");
//Console.WriteLine (" ftime: {0}", ftime);
//Console.WriteLine (" monod: {0}", monodoc_timestamp);
//Console.WriteLine (" handl: {0}", handler_timestamp);
//Console.WriteLine (" dtIf: {0}", dtIfModifiedSince);
if (dtIfModifiedSince < DateTime.UtcNow &&
ftime <= dtIfModifiedSince &&
monodoc_timestamp <= dtIfModifiedSince &&
handler_timestamp <= dtIfModifiedSince) {
context.Response.StatusCode = 304;
return;
}
}
} catch { }
long ticks = System.Math.Max (monodoc_timestamp.Ticks, handler_timestamp.Ticks);
if (lastHelpSourceTime != DateTime.MinValue) {
ticks = System.Math.Max (ticks, lastHelpSourceTime.Ticks);
DateTime lastWT = new DateTime (ticks).ToUniversalTime ();
context.Response.AddHeader ("Last-Modified", lastWT.ToString ("r"));
}
}
void HandleMonodocUrl (HttpContext context, string link)
{
if (link.StartsWith ("source-id:") &&
(link.EndsWith (".gif") || link.EndsWith (".jpeg") ||
link.EndsWith (".jpg") || link.EndsWith(".png"))){
switch (link.Substring (link.LastIndexOf ('.') + 1))
{
case "gif":
context.Response.ContentType = "image/gif";
break;
case "jpeg":
case "jpg":
context.Response.ContentType = "image/jpeg";
break;
case "png":
context.Response.ContentType = "image/png";
break;
default:
throw new Exception ("Internal error");
}
Stream s = Global.help_tree.GetImage (link);
if (s == null)
throw new HttpException (404, "File not found");
CheckLastModified (context);
if (context.Response.StatusCode == 304)
return;
s.CopyTo (context.Response.OutputStream);
return;
} else if (link.Equals ("root:", StringComparison.Ordinal) && File.Exists ("home.html")) {
context.Response.WriteFile ("home.html");
return;
}
if (Global.help_tree == null)
return;
Node n;
//Console.WriteLine ("Considering {0}", link);
string content = Global.help_tree.RenderUrl (link, out n);
CheckLastModified (context);
if (context.Response.StatusCode == 304){
//Console.WriteLine ("Keeping", link);
return;
}
PrintDocs (content, n, context, GetHelpSource (n));
}
void HandleFastSearchRequest (HttpContext context, string request)
{
if (string.IsNullOrWhiteSpace (request) || request.Length < 3) {
// Unprocessable entity
context.Response.StatusCode = 422;
return;
}
var searchIndex = Global.GetSearchIndex ();
var result = searchIndex.FastSearch (request, 15);
// return Json corresponding to the results
var answer = result == null || result.Count == 0 ? "[]" : "[" +
Enumerable.Range (0, result.Count)
.Select (i => string.Format ("{{ \"name\" : \"{0}\", \"url\" : \"{1}\", \"fulltitle\" : \"{2}\" }}",
result.GetTitle (i), result.GetUrl (i), result.GetFullTitle (i)))
.Aggregate ((e1, e2) => e1 + ", " + e2) + "]";
Console.WriteLine ("answer is {0}", answer);
context.Response.ContentType = "application/json";
context.Response.Write (answer);
}
void HandleFullSearchRequest (HttpContext context, string request)
{
if (string.IsNullOrWhiteSpace (request)) {
// Unprocessable entity
context.Response.StatusCode = 422;
return;
}
int start = 0, count = 0;
var searchIndex = Global.GetSearchIndex ();
Result result = null;
if (int.TryParse (context.Request.Params["count"], out count)) {
if (int.TryParse (context.Request.Params["start"], out start))
result = searchIndex.Search (request, count, start);
else
result = searchIndex.Search (request, count);
} else {
count = 20;
result = searchIndex.Search (request, count);
}
// return Json corresponding to the results
var answer = result == null || result.Count == 0 ? "[]" : "[" +
Enumerable.Range (0, result.Count)
.Select (i => string.Format ("{{ \"name\" : \"{0}\", \"url\" : \"{1}\", \"fulltitle\" : \"{2}\" }}",
result.GetTitle (i), result.GetUrl (i), result.GetFullTitle (i)))
.Aggregate ((e1, e2) => e1 + ", " + e2) + "]";
answer = string.Format ("{{ \"count\": {0}, \"start\": {1}, \"result\": {2} }}", count, start, answer);
Console.WriteLine ("answer is {0}", answer);
context.Response.ContentType = "application/json";
context.Response.Write (answer);
}
HelpSource GetHelpSource (Node n)
{
if (n != null)
return n.tree.HelpSource;
return null;
}
string requestPath;
void PrintDocs (string content, Node node, HttpContext ctx, HelpSource hs)
{
string tree_path = string.Empty;
Node current = node;
while (current != null && current.Parent != null) {
int index = current.Parent.Nodes.IndexOf (current);
tree_path = '@' + (index + tree_path);
current = current.Parent;
}
tree_path = tree_path.Length > 0 ? tree_path.Substring (1) : tree_path;
Console.WriteLine ("Tree path is:" + tree_path);
string title = (node == null || node.Caption == null) ? "Mono XDocumentation" : node.Caption;
ctx.Response.Write (@"
<html>
<head>
<link type='text/css' rel='stylesheet' href='common.css' media='all' title='Default style' />
<meta name='TreePath' value='");
ctx.Response.Write (tree_path);
ctx.Response.Write (@"' />
<script type='text/javascript'>
<!--
function login (rurl)
{
document.location.href = 'login.aspx?ReturnUrl=' + rurl;
}
function load ()
{
// If topic loaded in a window by itself, load index.aspx with the same set of params.
if (top.location == document.location)
{
top.location.href = 'index.aspx'+document.location.search;
}
objs = document.getElementsByTagName('img');
for (i = 0; i < objs.length; i++)
{
e = objs [i];
if (e.src == null) continue;
objs[i].src = makeLink (objs[i].src);
}
}
function makeLink (link)
{
if (link == '') return '';
if (link.charAt(0) == '#') return link;
protocol = link.substring (0, link.indexOf (':'));
switch (protocol)
{
case 'http':
case 'ftp':
case 'mailto':
case 'javascript':
case 'https':
return link;
default:
if(document.all) {
return '");
ctx.Response.Write (ctx.Request.Path);
ctx.Response.Write (@"?link=' + link.replace(/\+/g, '%2B').replace(/file:\/\/\//, '');
}
return '");
ctx.Response.Write (ctx.Request.Path);
ctx.Response.Write (@"?link=' + link.replace(/\+/g, '%2B');
}
}
-->
");
if (!string.IsNullOrEmpty (Global.ua)) {
ctx.Response.Write (@"var _gaq = _gaq || [];
_gaq.push(['_setAccount', '");
ctx.Response.Write (Global.ua);
ctx.Response.Write (@"']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type =
'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' :
'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();");
}
ctx.Response.Write ("</script><title>");
ctx.Response.Write (title);
ctx.Response.Write ("</title>\n");
if (hs != null && hs.InlineCss != null) {
ctx.Response.Write ("<style type=\"text/css\">\n");
ctx.Response.Write (hs.InlineCss);
ctx.Response.Write ("</style>\n");
}
if (hs != null && hs.InlineJavaScript != null) {
ctx.Response.Write ("<script type=\"text/JavaScript\">\n");
ctx.Response.Write (hs.InlineJavaScript);
ctx.Response.Write ("</script>\n");
}
ctx.Response.Write (@"</head><body onLoad='load()'>");
// Set up object variable, as it's required by the MakeLink delegate
requestPath=ctx.Request.Path;
string output;
if (content == null)
output = "No documentation available on this topic";
else
output = MakeLinks(content);
ctx.Response.Write (output);
ctx.Response.Write (@"</body></html>");
}
string MakeLinks(string content)
{
MatchEvaluator linkUpdater=new MatchEvaluator(MakeLink);
if(content.Trim().Length<1|| content==null)
return content;
try
{
string updatedContents=Regex.Replace(content,"(<a[^>]*href=['\"])([^'\"]+)(['\"][^>]*)(>)", linkUpdater);
return(updatedContents);
}
catch(Exception e)
{
return "LADEDA" + content+"!<!--Exception:"+e.Message+"-->";
}
}
// Delegate to be called from MakeLinks for fixing <a> tag
string MakeLink (Match theMatch)
{
string updated_link = null;
// Return the link without change if it of the form
// $protocol:... or #...
string link = theMatch.Groups[2].ToString();
if (Regex.Match(link, @"^\w+:\/\/").Success || Regex.Match(link, "^#").Success ||
Regex.Match(link, @"^javascript:").Success)
updated_link = theMatch.Groups[0].ToString();
else if (link.StartsWith ("edit:")){
link = link.Substring (5);
updated_link = String.Format("{0}/edit.aspx?link={2}{3} target=\"content\"{4}",
theMatch.Groups[1].ToString(),
requestPath,
HttpUtility.UrlEncode (link.Replace ("file://","")),
theMatch.Groups[3].ToString(),
theMatch.Groups[4].ToString());
} else {
updated_link = String.Format ("{0}{1}?link={2}{3} onclick=\"window.parent.change_page('{2}')\" {4}",
theMatch.Groups[1].ToString(),
requestPath,
HttpUtility.UrlEncode (link.Replace ("file://","")),
theMatch.Groups[3].ToString(),
theMatch.Groups[4].ToString());
}
return updated_link;
}
bool IHttpHandler.IsReusable
{
get {
return true;
}
}
}
}
Jump to Line
Something went wrong with that request. Please try again.