moel@348: /*
moel@348:  
moel@348:   This Source Code Form is subject to the terms of the Mozilla Public
moel@348:   License, v. 2.0. If a copy of the MPL was not distributed with this
moel@348:   file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@348:  
moel@348: 	Copyright (C) 2012 Prince Samuel <prince.samuel@gmail.com>
moel@348: 
moel@348: */
moel@348: 
moel@348: using System;
moel@348: using System.Collections.Generic;
moel@348: using System.Text;
moel@348: using System.Net;
moel@348: using System.Threading;
moel@348: using System.IO;
moel@348: using OpenHardwareMonitor.GUI;
moel@348: using OpenHardwareMonitor.Hardware;
moel@348: using System.Reflection;
moel@348: using System.Drawing;
moel@348: using System.Drawing.Imaging;
moel@348: 
moel@348: namespace OpenHardwareMonitor.Utilities
moel@348: {
moel@348:     public class HttpServer
moel@348:     {
moel@348:         private HttpListener listener;
moel@348:         private int listenerPort, nodeCount;
moel@348:         private Thread listenerThread;
moel@348:         private Node root;
moel@348: 
moel@348:         public HttpServer(Node r, int p)
moel@348:         {
moel@348:             root = r;
moel@348:             listenerPort = p;
moel@348:             //JSON node count. 
moel@348:             nodeCount = 0;
moel@348:             listener = new HttpListener();
moel@348:         }
moel@348: 
moel@348:         public Boolean startHTTPListener()
moel@348:         {
moel@348:             try
moel@348:             {
moel@348:                 if (listener.IsListening)
moel@348:                     return true;
moel@348: 
moel@348:                 string prefix = "http://+:" + listenerPort + "/";
moel@348:                 listener.Prefixes.Clear();
moel@348:                 listener.Prefixes.Add(prefix);
moel@348:                 listener.Start();
moel@348: 
moel@348:                 if (listenerThread == null)
moel@348:                 {
moel@348:                     listenerThread = new Thread(HandleRequests);
moel@348:                     listenerThread.Start();
moel@348:                 }
moel@348:             }
moel@348:             catch (Exception e)
moel@348:             {
moel@348:                 return false;
moel@348:             }
moel@348: 
moel@348:             return true;
moel@348:         }
moel@348: 
moel@348:         public Boolean stopHTTPListener()
moel@348:         {
moel@348:             try
moel@348:             {
moel@348:                 listenerThread.Abort();
moel@348:                 listener.Stop();
moel@348:                 listenerThread = null;
moel@348:             }
moel@348:             catch (System.Net.HttpListenerException e)
moel@348:             {
moel@348:             }
moel@348:             catch (System.Threading.ThreadAbortException e)
moel@348:             {
moel@348:             }
moel@348:             catch (System.NullReferenceException e)
moel@348:             {
moel@348:             }
moel@348:             catch (Exception e)
moel@348:             {
moel@348:             }
moel@348:             return true;
moel@348:         }
moel@348: 
moel@348:         public void HandleRequests()
moel@348:         {
moel@348: 
moel@348:             while (listener.IsListening)
moel@348:             {
moel@348:                 var context = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
moel@348:                 context.AsyncWaitHandle.WaitOne();
moel@348:             }
moel@348:         }
moel@348: 
moel@348:         public void ListenerCallback(IAsyncResult result)
moel@348:         {
moel@348:             HttpListener listener = (HttpListener)result.AsyncState;
moel@348:             if (listener == null || !listener.IsListening)
moel@348:                 return;
moel@348:             // Call EndGetContext to complete the asynchronous operation.
moel@348:             HttpListenerContext context = listener.EndGetContext(result);
moel@348:             HttpListenerRequest request = context.Request;
moel@348: 
moel@348:             var requestedFile = request.RawUrl.Substring(1);
moel@348:             if (requestedFile == "data.json")
moel@348:             {
moel@348:                 sendJSON(context);
moel@348:                 return;
moel@348:             }
moel@348: 
moel@348:             if (requestedFile.Contains("images_icon"))
moel@348:             {
moel@348:                 serveResourceImage(context, requestedFile.Replace("images_icon/", ""));
moel@348:                 return;
moel@348:             }
moel@348: 
moel@348:             //default file to be served
moel@348:             if (string.IsNullOrEmpty(requestedFile))
moel@348:                 requestedFile = "index.html";
moel@348: 
moel@348:             string[] splits = requestedFile.Split('.');
moel@348:             string ext = splits[splits.Length - 1];
moel@348:             serveResourceFile(context, "Web." + requestedFile.Replace('/', '.'), ext);
moel@348:         }
moel@348: 
moel@348:         private void serveResourceFile(HttpListenerContext context ,string name, string ext) {
moel@348: 
moel@348:           //hack! resource names do not support the hyphen
moel@348:           name = "OpenHardwareMonitor.Resources." + name.Replace("custom-theme", "custom_theme");
moel@348:     
moel@348:           string[] names = 
moel@348:             Assembly.GetExecutingAssembly().GetManifestResourceNames();
moel@348:           for (int i = 0; i < names.Length; i++) {
moel@348:             if (names[i].Replace('\\', '.') == name) {
moel@348:               using (Stream stream = Assembly.GetExecutingAssembly().
moel@348:                 GetManifestResourceStream(names[i])) {
moel@348:                     context.Response.ContentType = getcontentType("." + ext);
moel@348:                     context.Response.ContentLength64 = stream.Length;
moel@348:                     byte[] buffer = new byte[512 * 1024];
moel@348:                     int len;
moel@348:                     while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
moel@348:                     {
moel@348:                         context.Response.OutputStream.Write(buffer, 0, len);
moel@348:                     }
moel@348:                     context.Response.OutputStream.Close();
moel@348:                 }
moel@348:               return;
moel@348:             }
moel@348:           }
moel@348:           context.Response.OutputStream.Close();
moel@348:           context.Response.StatusCode = 404;
moel@348:           context.Response.Close();
moel@348:         }
moel@348: 
moel@348:         private void serveResourceImage(HttpListenerContext context ,string name) {
moel@348:           name = "OpenHardwareMonitor.Resources." + name;
moel@348:     
moel@348:           string[] names = 
moel@348:             Assembly.GetExecutingAssembly().GetManifestResourceNames();
moel@348:           for (int i = 0; i < names.Length; i++) {
moel@348:             if (names[i].Replace('\\', '.') == name) {
moel@348:               using (Stream stream = Assembly.GetExecutingAssembly().
moel@348:                 GetManifestResourceStream(names[i])) {
moel@348:     
moel@348:                 Image image = Image.FromStream(stream);
moel@348:                 context.Response.ContentType = "image/png";
moel@348:                 using (MemoryStream ms = new MemoryStream())
moel@348:                 {
moel@348:                     image.Save(ms, ImageFormat.Png);
moel@348:                     ms.WriteTo(context.Response.OutputStream);
moel@348:                 }
moel@348:                 context.Response.OutputStream.Close();
moel@348:                 image.Dispose();
moel@348:                 return;
moel@348:               }
moel@348:             }
moel@348:           } 
moel@348:           context.Response.OutputStream.Close();
moel@348:           context.Response.StatusCode = 404;
moel@348:           context.Response.Close();
moel@348:         }
moel@348: 
moel@348:         private void sendJSON(HttpListenerContext context)
moel@348:         {
moel@348: 
moel@348:             string JSON = "{\"id\": 0, \"Text\": \"Sensor\", \"Children\": [";
moel@348:             nodeCount = 1;
moel@348:             JSON += generateJSON(root);
moel@348:             JSON += "]";
moel@348:             JSON += ", \"Min\": \"Min\"";
moel@348:             JSON += ", \"Value\": \"Value\"";
moel@348:             JSON += ", \"Max\": \"Max\"";
moel@348:             JSON += ", \"ImageURL\": \"\"";
moel@348:             JSON += "}";
moel@348: 
moel@348:             var responseContent = JSON;
moel@348:             byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
moel@348: 
moel@348:             context.Response.ContentLength64 = buffer.Length;
moel@348:             context.Response.ContentType = "application/json";
moel@348: 
moel@348:             Stream outputStream = context.Response.OutputStream;
moel@348:             outputStream.Write(buffer, 0, buffer.Length);
moel@348:             outputStream.Close();
moel@348: 
moel@348:         }
moel@348: 
moel@348:         private string generateJSON(Node n)
moel@348:         {
moel@348:             string JSON = "{\"id\": " + nodeCount + ", \"Text\": \"" + n.Text + "\", \"Children\": [";
moel@348:             nodeCount++;
moel@348: 
moel@348:             foreach (Node child in n.Nodes)
moel@348:                 JSON += generateJSON(child) + ", ";
moel@348:             if (JSON.EndsWith(", "))
moel@348:                 JSON = JSON.Remove(JSON.LastIndexOf(","));
moel@348:             JSON += "]";
moel@348: 
moel@348:             if (n is SensorNode)
moel@348:             {
moel@348:                 JSON += ", \"Min\": \"" + ((SensorNode)n).Min + "\"";
moel@348:                 JSON += ", \"Value\": \"" + ((SensorNode)n).Value + "\"";
moel@348:                 JSON += ", \"Max\": \"" + ((SensorNode)n).Max + "\"";
moel@348:                 JSON += ", \"ImageURL\": \"images/transparent.png\"";
moel@348:             }
moel@348:             else if (n is HardwareNode)
moel@348:             {
moel@348:                 JSON += ", \"Min\": \"\"";
moel@348:                 JSON += ", \"Value\": \"\"";
moel@348:                 JSON += ", \"Max\": \"\"";
moel@348:                 JSON += ", \"ImageURL\": \"images_icon/" + getHardwareImageFile((HardwareNode)n) + "\"";
moel@348:             }
moel@348:             else if (n is TypeNode)
moel@348:             {
moel@348:                 JSON += ", \"Min\": \"\"";
moel@348:                 JSON += ", \"Value\": \"\"";
moel@348:                 JSON += ", \"Max\": \"\"";
moel@348:                 JSON += ", \"ImageURL\": \"images_icon/" + getTypeImageFile((TypeNode)n) + "\"";
moel@348:             }
moel@348:             else
moel@348:             {
moel@348:                 JSON += ", \"Min\": \"\"";
moel@348:                 JSON += ", \"Value\": \"\"";
moel@348:                 JSON += ", \"Max\": \"\"";
moel@348:                 JSON += ", \"ImageURL\": \"images_icon/computer.png\"";
moel@348:             }
moel@348: 
moel@348:             JSON += "}";
moel@348:             return JSON;
moel@348:         }
moel@348: 
moel@348:         private static void returnFile(HttpListenerContext context, string filePath)
moel@348:         {
moel@348:             context.Response.ContentType = getcontentType(Path.GetExtension(filePath));
moel@348:             const int bufferSize = 1024 * 512; //512KB
moel@348:             var buffer = new byte[bufferSize];
moel@348:             using (var fs = File.OpenRead(filePath))
moel@348:             {
moel@348:                 
moel@348:                 context.Response.ContentLength64 = fs.Length;
moel@348:                 int read;
moel@348:                 while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
moel@348:                     context.Response.OutputStream.Write(buffer, 0, read);
moel@348:             }
moel@348: 
moel@348:             context.Response.OutputStream.Close();
moel@348:         }
moel@348: 
moel@348:         private static string getcontentType(string extension)
moel@348:         {
moel@348:             switch (extension)
moel@348:             {
moel@348:                 case ".avi": return "video/x-msvideo";
moel@348:                 case ".css": return "text/css";
moel@348:                 case ".doc": return "application/msword";
moel@348:                 case ".gif": return "image/gif";
moel@348:                 case ".htm":
moel@348:                 case ".html": return "text/html";
moel@348:                 case ".jpg":
moel@348:                 case ".jpeg": return "image/jpeg";
moel@348:                 case ".js": return "application/x-javascript";
moel@348:                 case ".mp3": return "audio/mpeg";
moel@348:                 case ".png": return "image/png";
moel@348:                 case ".pdf": return "application/pdf";
moel@348:                 case ".ppt": return "application/vnd.ms-powerpoint";
moel@348:                 case ".zip": return "application/zip";
moel@348:                 case ".txt": return "text/plain";
moel@348:                 default: return "application/octet-stream";
moel@348:             }
moel@348:         }
moel@348: 
moel@348:         private static string getHardwareImageFile(HardwareNode hn)
moel@348:         {
moel@348: 
moel@348:             switch (hn.Hardware.HardwareType)
moel@348:             {
moel@348:                 case HardwareType.CPU:
moel@348:                     return "cpu.png";
moel@348:                 case HardwareType.GpuNvidia:
moel@348:                     return "nvidia.png";
moel@348:                 case HardwareType.GpuAti:
moel@348:                     return "ati.png";
moel@348:                 case HardwareType.HDD:
moel@348:                     return "hdd.png";
moel@348:                 case HardwareType.Heatmaster:
moel@348:                     return "bigng.png";
moel@348:                 case HardwareType.Mainboard:
moel@348:                     return "mainboard.png";
moel@348:                 case HardwareType.SuperIO:
moel@348:                     return "chip.png";
moel@348:                 case HardwareType.TBalancer:
moel@348:                     return "bigng.png";
moel@348:                 default:
moel@348:                     return "cpu.png";
moel@348:             }
moel@348: 
moel@348:         }
moel@348: 
moel@348:         private static string getTypeImageFile(TypeNode tn)
moel@348:         {
moel@348: 
moel@348:             switch (tn.SensorType)
moel@348:             {
moel@348:                 case SensorType.Voltage:
moel@348:                     return "voltage.png";
moel@348:                 case SensorType.Clock:
moel@348:                     return "clock.png";
moel@348:                 case SensorType.Load:
moel@348:                     return "load.png";
moel@348:                 case SensorType.Temperature:
moel@348:                     return "temperature.png";
moel@348:                 case SensorType.Fan:
moel@348:                     return "fan.png";
moel@348:                 case SensorType.Flow:
moel@348:                     return "flow.png";
moel@348:                 case SensorType.Control:
moel@348:                     return "control.png";
moel@348:                 case SensorType.Level:
moel@348:                     return "level.png";
moel@348:                 case SensorType.Power:
moel@348:                     return "power.png";
moel@348:                 default:
moel@348:                     return "power.png";
moel@348:             }
moel@348: 
moel@348:         }
moel@348: 
moel@348:         public int ListenerPort
moel@348:         {
moel@348:             get { return listenerPort; }
moel@348:             set { listenerPort = value; }
moel@348:         }
moel@348: 
moel@348:         ~HttpServer()
moel@348:         {
moel@348:             stopHTTPListener();
moel@348:             listener.Abort();
moel@348:         }
moel@348: 
moel@348:         public void Quit()
moel@348:         {
moel@348:             stopHTTPListener();
moel@348:             listener.Abort();
moel@348:         }
moel@348:     }
moel@348: }