namespace Nwc.XmlRpc { using System; using System.Collections; using System.Reflection; /// XML-RPC System object implementation of extended specifications. [XmlRpcExposed] public class XmlRpcSystemObject { private XmlRpcServer _server; static private IDictionary _methodHelp = new Hashtable(); /// Static IDictionary to hold mappings of method name to associated documentation String static public IDictionary MethodHelp { get { return _methodHelp; } } /// Constructor. /// XmlRpcServer server to be the system object for. public XmlRpcSystemObject(XmlRpcServer server) { _server = server; server.Add("system", this); _methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description."); } /// Invoke a method on a given object. /// Using reflection, and respecting the XmlRpcExposed attribute, /// invoke the methodName method on the target /// instance with the parameters provided. All this packages other Invoke methods /// end up calling this. /// Object the value the invoked method returns. /// If method does not exist, is not exposed, parameters invalid, or invocation /// results in an exception. Note, the XmlRpcException.Code will indicate cause. static public Object Invoke(Object target, String methodName, IList parameters) { if (target == null) throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object."); Type type = target.GetType(); MethodInfo method = type.GetMethod(methodName); try { if (!XmlRpcExposedAttribute.ExposedMethod(target, methodName)) throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed."); } catch (MissingMethodException me) { throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message); } Object[] args = new Object[parameters.Count]; int index = 0; foreach (Object arg in parameters) { args[index] = arg; index++; } try { Object retValue = method.Invoke(target, args); if (retValue == null) throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL."); return retValue; } catch (XmlRpcException e) { throw e; } catch (ArgumentException ae) { Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message, LogLevel.Information); String call = methodName + "( "; foreach (Object o in args) { call += o.GetType().Name; call += " "; } call += ")"; throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call); } catch (TargetParameterCountException tpce) { Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message, LogLevel.Information); throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName); } catch (TargetInvocationException tie) { throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message); } } /// List methods available on all handlers of this server. /// IList An array of Strings, each String will have form "object.method". [XmlRpcExposed] public IList listMethods() { IList methods = new ArrayList(); Boolean considerExposure; foreach (DictionaryEntry handlerEntry in _server) { considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType()); foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers()) { if (mi.MemberType != MemberTypes.Method) continue; if (!((MethodInfo)mi).IsPublic) continue; if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi)) continue; methods.Add(handlerEntry.Key + "." + mi.Name); } } return methods; } /// Given a method name return the possible signatures for it. /// String The object.method name to look up. /// IList Of arrays of signatures. [XmlRpcExposed] public IList methodSignature(String name) { IList signatures = new ArrayList(); int index = name.IndexOf('.'); if (index < 0) return signatures; String oName = name.Substring(0, index); Object obj = _server[oName]; if (obj == null) return signatures; MemberInfo[] mi = obj.GetType().GetMember(name.Substring(index + 1)); if (mi == null || mi.Length != 1) // for now we want a single signature return signatures; MethodInfo method; try { method = (MethodInfo)mi[0]; } catch (Exception e) { Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e, LogLevel.Information); return signatures; } if (!method.IsPublic) return signatures; IList signature = new ArrayList(); signature.Add(method.ReturnType.Name); foreach (ParameterInfo param in method.GetParameters()) { signature.Add(param.ParameterType.Name); } signatures.Add(signature); return signatures; } /// Help for given method signature. Not implemented yet. /// String The object.method name to look up. /// String help text. Rich HTML text. [XmlRpcExposed] public String methodHelp(String name) { String help = null; try { help = (String)_methodHelp[_server.MethodName(name)]; } catch (XmlRpcException e) { throw e; } catch (Exception) { /* ignored */ }; if (help == null) help = "No help available for: " + name; return help; } /// Boxcarring support method. /// IList of calls /// ArrayList of results/faults. [XmlRpcExposed] public IList multiCall(IList calls) { IList responses = new ArrayList(); XmlRpcResponse fault = new XmlRpcResponse(); foreach (IDictionary call in calls) { try { XmlRpcRequest req = new XmlRpcRequest((String)call[XmlRpcXmlTokens.METHOD_NAME], (ArrayList)call[XmlRpcXmlTokens.PARAMS]); Object results = _server.Invoke(req); IList response = new ArrayList(); response.Add(results); responses.Add(response); } catch (XmlRpcException e) { fault.SetFault(e.FaultCode, e.FaultString); responses.Add(fault.Value); } catch (Exception e2) { fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); responses.Add(fault.Value); } } return responses; } } }