using System; using System.Collections.Generic; using System.IO; using System.Linq; using ln.templates; using System.Collections; using ln.types; namespace ln.http.resources { public delegate Resource ResourceTypeHookDelegate(DirectoryResource directoryResource, FileInfo fileInfo); public class DirectoryResource : Resource, TemplateProvider { public ResourceTypeHookDelegate ResourceTypeHook { get; set; } protected Dictionary cache = new Dictionary(); protected Dictionary injectedResources = new Dictionary(); IEnumerable searchPaths; public DirectoryResource(IEnumerable search) : base("") { searchPaths = search; } public DirectoryResource(Resource container,IEnumerable search) : base(container, System.IO.Path.GetFileName(search.First())) { searchPaths = search; } public DirectoryResource(String path) : base("") { searchPaths = new string[] { path }; } public DirectoryResource(String path,string name) : base(name) { searchPaths = new string[] { path }; } public DirectoryResource(Resource container, String path) : base(container, System.IO.Path.GetFileName(path)) { searchPaths = new string[] { path }; if (container is DirectoryResource) { DirectoryResource parent = container as DirectoryResource; ResourceTypeHook = parent.ResourceTypeHook; } } public override bool Contains(string name) { if (injectedResources.ContainsKey(name)) return true; String cPath = PathHelper.FindPath(searchPaths.Select((p) => System.IO.Path.Combine(p, name))); lock (cache) { if ((cPath == null) && cache.ContainsKey(name)) cache.Remove(name); } return (cPath != null); } public override void AddResource(Resource resource) { lock (cache) { cache.Add(resource.Name, resource); } } public override void RemoveResource(Resource resource) { lock (cache) { if (cache.ContainsValue(resource)) cache.Remove(resource.Name); } } public void InjectResource(Resource resource) { injectedResources.Add(resource.Name, resource); } public override Resource GetResource(string name) { lock (cache) { if (!Contains(name)) return null; if (injectedResources.ContainsKey(name)) return injectedResources[name]; if (cache.ContainsKey(name)) return cache[name]; String cPath = PathHelper.FindPath(searchPaths.Select((p) => System.IO.Path.Combine(p, name))); if (cPath == null) return null; if (Directory.Exists(cPath)) { DirectoryResource child = new DirectoryResource(this, searchPaths.Select((p) => System.IO.Path.Combine(p, name))); child.ResourceTypeHook = ResourceTypeHook; return child; } else if (File.Exists(cPath)) { FileInfo fileInfo = new FileInfo(cPath); Resource resource = null; if (ResourceTypeHook != null) { resource = ResourceTypeHook(this, fileInfo); } if (resource == null) { resource = new FileResource(this, fileInfo); } return resource; } } return GetFallBackResource(); } public override IEnumerable GetResources() { return searchPaths.SelectMany((p) => new DirectoryInfo(p).EnumerateFileSystemInfos().Select((x) => GetResource(x.Name))).Distinct(); } public override HttpResponse GetResponse(HttpRequest httpRequest) { if (DefaultResource != null) return httpRequest.Redirect(String.Join("/", DefaultResource.Path)); Resource fallback = GetFallBackResource(); if (fallback != null) { return fallback.GetResponse(httpRequest); } else { throw new NotImplementedException(); } } public Template FindTemplate(string templatePath) { if ((templatePath[0] == '/') && (Container is DirectoryResource)) { return RootDirectoryResource.FindTemplate(templatePath); } Resource current = this; Queue path = new Queue(templatePath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)); while (path.Count > 0) { current = current.GetResource(path.Dequeue()); } if (current is TemplateResource) { return (current as TemplateResource).Template; } if (Container is TemplateProvider) { return (Container as TemplateProvider).FindTemplate(templatePath); } return null; } public DirectoryResource RootDirectoryResource { get { if (Container is DirectoryResource) { DirectoryResource parent = Container as DirectoryResource; return parent.RootDirectoryResource; } return this; } } } public class FileResource : DirectoryResource { DirectoryResource DirectoryResource { get; } public FileInfo FileInfo { get; } public FileResource(DirectoryResource directoryResource,FileInfo fileInfo) :base(directoryResource,fileInfo.Name) { DirectoryResource = directoryResource; FileInfo = fileInfo; } public override bool Contains(string name) { return false; } public override void AddResource(Resource resource) { throw new NotImplementedException(); } public override void RemoveResource(Resource resource) { throw new NotImplementedException(); } public override IEnumerable GetResources() { return new Resource[0]; } public override HttpResponse GetResponse(HttpRequest httpRequest) { if (!httpRequest.Method.Equals("GET")) throw new NotImplementedException(String.Format("Method {0} is not supported by this resource", httpRequest.Method)); HttpResponse response = new HttpResponse(httpRequest, FileInfo.OpenRead()); response.SetHeader("content-type", GuessContentType()); return response; } public string GuessContentType() { String ext = FileInfo.Extension; switch (ext) { case ".html": return "text/html"; case ".txt": return "text/plain"; case ".css": return "text/css"; case ".js": return "text/javascript"; default: return "application/octet-stream"; } } } }