%@ 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 ("
Unknown request"); 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 (@"