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; /// A restricted HTTP server for use with XML-RPC. /// 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. /// 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 ///Constructor with port and address. ///This constructor sets up a TcpListener listening on the ///given port and address. It also calls a Thread on the method StartListen(). ///IPAddress value of the address to listen on. ///Int value of the port to listen on. public XmlRpcServer(IPAddress address, int port) { _port = port; _address = address; _handlers = new Hashtable(); _system = new XmlRpcSystemObject(this); _wc = new WaitCallback(WaitCallback); } ///Basic constructor. ///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. ///Int value of the port to listen on. public XmlRpcServer(int port) : this(IPAddress.Any, port) { } /// Start the server. 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); } } /// Stop the server. 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); } } /// Get an enumeration of my XML-RPC handlers. /// IEnumerable the handler enumeration. public IEnumerator GetEnumerator() { return _handlers.GetEnumerator(); } /// Retrieve a handler by name. /// String naming a handler /// Object that is the handler. public Object this[String name] { get { return _handlers[name]; } } /// ///This method Accepts new connections and dispatches them when appropriate. /// public void StartListen() { while (true && _myListener != null) { //Accept a new connection XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient()); ThreadPool.QueueUserWorkItem(_wc, responder); } } /// ///Add an XML-RPC handler object by name. /// ///String XML-RPC dispatch name of this object. ///Object The object that is the XML-RPC handler. public void Add(String name, Object obj) { _handlers.Add(name, obj); } ///Return a C# object.method name for and XML-RPC object.method name pair. ///The XML-RPC object.method. ///String of form object.method for the underlying C# method. 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); } ///Invoke a method described in a request. ///XmlRpcRequest containing a method descriptions. /// /// public Object Invoke(XmlRpcRequest req) { return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params); } ///Invoke a method on a named handler. ///String The name of the handler. ///String The name of the method to invoke on the handler. ///IList The parameters to invoke the method with. /// 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); } /// The method the thread pool invokes when a thread is available to handle an HTTP request. /// TcpClient from the socket accept. 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(); } /// /// This function send the Header Information to the client (Browser) /// /// HTTP Version /// Mime Type /// Total Bytes to be sent in the body /// /// Socket reference 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); } } }