namespace Nwc.XmlRpc { using System; using System.Collections; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Xml; /// <summary>A restricted HTTP server for use with XML-RPC.</summary> /// <remarks>It only handles POST requests, and only POSTs representing XML-RPC calls. /// In addition to dispatching requests it also provides a registry for request handlers. /// </remarks> public class XmlRpcServer : IEnumerable { #pragma warning disable 0414 // disable "private field assigned but not used" const int RESPONDER_COUNT = 10; private TcpListener _myListener; private int _port; private IPAddress _address; private IDictionary _handlers; private XmlRpcSystemObject _system; private WaitCallback _wc; #pragma warning restore 0414 ///<summary>Constructor with port and address.</summary> ///<remarks>This constructor sets up a TcpListener listening on the ///given port and address. It also calls a Thread on the method StartListen().</remarks> ///<param name="address"><c>IPAddress</c> value of the address to listen on.</param> ///<param name="port"><c>Int</c> value of the port to listen on.</param> public XmlRpcServer(IPAddress address, int port) { _port = port; _address = address; _handlers = new Hashtable(); _system = new XmlRpcSystemObject(this); _wc = new WaitCallback(WaitCallback); } ///<summary>Basic constructor.</summary> ///<remarks>This constructor sets up a TcpListener listening on the ///given port. It also calls a Thread on the method StartListen(). IPAddress.Any ///is assumed as the address here.</remarks> ///<param name="port"><c>Int</c> value of the port to listen on.</param> public XmlRpcServer(int port) : this(IPAddress.Any, port) { } /// <summary>Start the server.</summary> public void Start() { try { Stop(); //start listing on the given port // IPAddress addr = IPAddress.Parse("127.0.0.1"); lock (this) { _myListener = new TcpListener(IPAddress.Any, _port); _myListener.Start(); //start the thread which calls the method 'StartListen' Thread th = new Thread(new ThreadStart(StartListen)); th.Start(); } } catch (Exception e) { Logger.WriteEntry("An Exception Occurred while Listening :" + e.ToString(), LogLevel.Error); } } /// <summary>Stop the server.</summary> public void Stop() { try { if (_myListener != null) { lock (this) { _myListener.Stop(); _myListener = null; } } } catch (Exception e) { Logger.WriteEntry("An Exception Occurred while stopping :" + e.ToString(), LogLevel.Error); } } /// <summary>Get an enumeration of my XML-RPC handlers.</summary> /// <returns><c>IEnumerable</c> the handler enumeration.</returns> public IEnumerator GetEnumerator() { return _handlers.GetEnumerator(); } /// <summary>Retrieve a handler by name.</summary> /// <param name="name"><c>String</c> naming a handler</param> /// <returns><c>Object</c> that is the handler.</returns> public Object this[String name] { get { return _handlers[name]; } } ///<summary> ///This method Accepts new connections and dispatches them when appropriate. ///</summary> public void StartListen() { while (true && _myListener != null) { //Accept a new connection XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient()); ThreadPool.QueueUserWorkItem(_wc, responder); } } ///<summary> ///Add an XML-RPC handler object by name. ///</summary> ///<param name="name"><c>String</c> XML-RPC dispatch name of this object.</param> ///<param name="obj"><c>Object</c> The object that is the XML-RPC handler.</param> public void Add(String name, Object obj) { _handlers.Add(name, obj); } ///<summary>Return a C# object.method name for and XML-RPC object.method name pair.</summary> ///<param name="methodName">The XML-RPC object.method.</param> ///<returns><c>String</c> of form object.method for the underlying C# method.</returns> public String MethodName(String methodName) { int dotAt = methodName.LastIndexOf('.'); if (dotAt == -1) { throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName); } String objectName = methodName.Substring(0, dotAt); Object target = _handlers[objectName]; if (target == null) { throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); } return target.GetType().FullName + "." + methodName.Substring(dotAt + 1); } ///<summary>Invoke a method described in a request.</summary> ///<param name="req"><c>XmlRpcRequest</c> containing a method descriptions.</param> /// <seealso cref="XmlRpcSystemObject.Invoke"/> /// <seealso cref="XmlRpcServer.Invoke(String,String,IList)"/> public Object Invoke(XmlRpcRequest req) { return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params); } ///<summary>Invoke a method on a named handler.</summary> ///<param name="objectName"><c>String</c> The name of the handler.</param> ///<param name="methodName"><c>String</c> The name of the method to invoke on the handler.</param> ///<param name="parameters"><c>IList</c> The parameters to invoke the method with.</param> /// <seealso cref="XmlRpcSystemObject.Invoke"/> public Object Invoke(String objectName, String methodName, IList parameters) { Object target = _handlers[objectName]; if (target == null) { throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); } return XmlRpcSystemObject.Invoke(target, methodName, parameters); } /// <summary>The method the thread pool invokes when a thread is available to handle an HTTP request.</summary> /// <param name="responder">TcpClient from the socket accept.</param> public void WaitCallback(object responder) { XmlRpcResponder resp = (XmlRpcResponder)responder; if (resp.HttpReq.HttpMethod == "POST") { try { resp.Respond(); } catch (Exception e) { Logger.WriteEntry("Failed on post: " + e, LogLevel.Error); } } else { Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod + " ignored", LogLevel.Error); } resp.Close(); } /// <summary> /// This function send the Header Information to the client (Browser) /// </summary> /// <param name="sHttpVersion">HTTP Version</param> /// <param name="sMIMEHeader">Mime Type</param> /// <param name="iTotBytes">Total Bytes to be sent in the body</param> /// <param name="sStatusCode"></param> /// <param name="output">Socket reference</param> static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output) { String sBuffer = ""; // if Mime type is not provided set default to text/html if (sMIMEHeader.Length == 0) { sMIMEHeader = "text/html"; // Default Mime Type is text/html } sBuffer += sHttpVersion + sStatusCode + "\r\n"; sBuffer += "Connection: close\r\n"; if (iTotBytes > 0) sBuffer += "Content-Length: " + iTotBytes + "\r\n"; sBuffer += "Server: XmlRpcServer \r\n"; sBuffer += "Content-Type: " + sMIMEHeader + "\r\n"; sBuffer += "\r\n"; output.Write(sBuffer); } } }