diff options
updated to use lastest version of libsl but is currently broke when using SL viewer 1.15.02, due to big changes in the message templates.
Diffstat (limited to 'XmlRpcCS')
-rw-r--r-- | XmlRpcCS/Logger.cs | 46 | ||||
-rw-r--r-- | XmlRpcCS/SimpleHttpRequest.cs | 204 | ||||
-rw-r--r-- | XmlRpcCS/XMLRPC.csproj | 138 | ||||
-rw-r--r-- | XmlRpcCS/XMLRPC.dll.build | 58 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcBoxcarRequest.cs | 51 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcClientProxy.cs | 61 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcDeserializer.cs | 195 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcErrorCodes.cs | 51 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcException.cs | 39 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcExposedAttribute.cs | 60 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcRequest.cs | 150 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcRequestDeserializer.cs | 64 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcRequestSerializer.cs | 51 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcResponder.cs | 98 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcResponse.cs | 85 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcResponseDeserializer.cs | 65 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcResponseSerializer.cs | 57 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcSerializer.cs | 109 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcServer.cs | 239 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcSystemObject.cs | 252 | ||||
-rw-r--r-- | XmlRpcCS/XmlRpcXmlTokens.cs | 76 |
21 files changed, 2149 insertions, 0 deletions
diff --git a/XmlRpcCS/Logger.cs b/XmlRpcCS/Logger.cs new file mode 100644 index 0000000..ebf804b --- /dev/null +++ b/XmlRpcCS/Logger.cs | |||
@@ -0,0 +1,46 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | |||
5 | /// <summary>Define levels of logging.</summary><remarks> This duplicates | ||
6 | /// similar enumerations in System.Diagnostics.EventLogEntryType. The | ||
7 | /// duplication was merited because .NET Compact Framework lacked the EventLogEntryType enum.</remarks> | ||
8 | public enum LogLevel | ||
9 | { | ||
10 | /// <summary>Information level, log entry for informational reasons only.</summary> | ||
11 | Information, | ||
12 | /// <summary>Warning level, indicates a possible problem.</summary> | ||
13 | Warning, | ||
14 | /// <summary>Error level, implies a significant problem.</summary> | ||
15 | Error | ||
16 | } | ||
17 | |||
18 | ///<summary> | ||
19 | ///Logging singleton with swappable output delegate. | ||
20 | ///</summary> | ||
21 | ///<remarks> | ||
22 | ///This singleton provides a centralized log. The actual WriteEntry calls are passed | ||
23 | ///off to a delegate however. Having a delegate do the actual logginh allows you to | ||
24 | ///implement different logging mechanism and have them take effect throughout the system. | ||
25 | ///</remarks> | ||
26 | public class Logger | ||
27 | { | ||
28 | ///<summary>Delegate definition for logging.</summary> | ||
29 | ///<param name="message">The message <c>String</c> to log.</param> | ||
30 | ///<param name="level">The <c>LogLevel</c> of your message.</param> | ||
31 | public delegate void LoggerDelegate(String message, LogLevel level); | ||
32 | ///<summary>The LoggerDelegate that will recieve WriteEntry requests.</summary> | ||
33 | static public LoggerDelegate Delegate = null; | ||
34 | |||
35 | ///<summary> | ||
36 | ///Method logging events are sent to. | ||
37 | ///</summary> | ||
38 | ///<param name="message">The message <c>String</c> to log.</param> | ||
39 | ///<param name="level">The <c>LogLevel</c> of your message.</param> | ||
40 | static public void WriteEntry(String message, LogLevel level) | ||
41 | { | ||
42 | if (Delegate != null) | ||
43 | Delegate(message, level); | ||
44 | } | ||
45 | } | ||
46 | } | ||
diff --git a/XmlRpcCS/SimpleHttpRequest.cs b/XmlRpcCS/SimpleHttpRequest.cs new file mode 100644 index 0000000..e5326c3 --- /dev/null +++ b/XmlRpcCS/SimpleHttpRequest.cs | |||
@@ -0,0 +1,204 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.IO; | ||
5 | using System.Net.Sockets; | ||
6 | using System.Collections; | ||
7 | |||
8 | ///<summary>Very basic HTTP request handler.</summary> | ||
9 | ///<remarks>This class is designed to accept a TcpClient and treat it as an HTTP request. | ||
10 | /// It will do some basic header parsing and manage the input and output streams associated | ||
11 | /// with the request.</remarks> | ||
12 | public class SimpleHttpRequest | ||
13 | { | ||
14 | private String _httpMethod = null; | ||
15 | private String _protocol; | ||
16 | private String _filePathFile = null; | ||
17 | private String _filePathDir = null; | ||
18 | private String __filePath; | ||
19 | private TcpClient _client; | ||
20 | private StreamReader _input; | ||
21 | private StreamWriter _output; | ||
22 | private Hashtable _headers; | ||
23 | |||
24 | /// <summary>A constructor which accepts the TcpClient.</summary> | ||
25 | /// <remarks>It creates the associated input and output streams, determines the request type, | ||
26 | /// and parses the remaining HTTP header.</remarks> | ||
27 | /// <param name="client">The <c>TcpClient</c> associated with the HTTP connection.</param> | ||
28 | public SimpleHttpRequest(TcpClient client) | ||
29 | { | ||
30 | _client = client; | ||
31 | _output = new StreamWriter(client.GetStream()); | ||
32 | _input = new StreamReader(client.GetStream()); | ||
33 | GetRequestMethod(); | ||
34 | GetRequestHeaders(); | ||
35 | } | ||
36 | |||
37 | /// <summary>The output <c>StreamWriter</c> associated with the request.</summary> | ||
38 | public StreamWriter Output | ||
39 | { | ||
40 | get { return _output; } | ||
41 | } | ||
42 | |||
43 | /// <summary>The input <c>StreamReader</c> associated with the request.</summary> | ||
44 | public StreamReader Input | ||
45 | { | ||
46 | get { return _input; } | ||
47 | } | ||
48 | |||
49 | /// <summary>The <c>TcpClient</c> with the request.</summary> | ||
50 | public TcpClient Client | ||
51 | { | ||
52 | get { return _client; } | ||
53 | } | ||
54 | |||
55 | private String _filePath | ||
56 | { | ||
57 | get { return __filePath; } | ||
58 | set | ||
59 | { | ||
60 | __filePath = value; | ||
61 | _filePathDir = null; | ||
62 | _filePathFile = null; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /// <summary>The type of HTTP request (i.e. PUT, GET, etc.).</summary> | ||
67 | public String HttpMethod | ||
68 | { | ||
69 | get { return _httpMethod; } | ||
70 | } | ||
71 | |||
72 | /// <summary>The level of the HTTP protocol.</summary> | ||
73 | public String Protocol | ||
74 | { | ||
75 | get { return _protocol; } | ||
76 | } | ||
77 | |||
78 | /// <summary>The "path" which is part of any HTTP request.</summary> | ||
79 | public String FilePath | ||
80 | { | ||
81 | get { return _filePath; } | ||
82 | } | ||
83 | |||
84 | /// <summary>The file portion of the "path" which is part of any HTTP request.</summary> | ||
85 | public String FilePathFile | ||
86 | { | ||
87 | get | ||
88 | { | ||
89 | if (_filePathFile != null) | ||
90 | return _filePathFile; | ||
91 | |||
92 | int i = FilePath.LastIndexOf("/"); | ||
93 | |||
94 | if (i == -1) | ||
95 | return ""; | ||
96 | |||
97 | i++; | ||
98 | _filePathFile = FilePath.Substring(i, FilePath.Length - i); | ||
99 | return _filePathFile; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | /// <summary>The directory portion of the "path" which is part of any HTTP request.</summary> | ||
104 | public String FilePathDir | ||
105 | { | ||
106 | get | ||
107 | { | ||
108 | if (_filePathDir != null) | ||
109 | return _filePathDir; | ||
110 | |||
111 | int i = FilePath.LastIndexOf("/"); | ||
112 | |||
113 | if (i == -1) | ||
114 | return ""; | ||
115 | |||
116 | i++; | ||
117 | _filePathDir = FilePath.Substring(0, i); | ||
118 | return _filePathDir; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | private void GetRequestMethod() | ||
123 | { | ||
124 | string req = _input.ReadLine(); | ||
125 | if (req == null) | ||
126 | throw new ApplicationException("Void request."); | ||
127 | |||
128 | if (0 == String.Compare("GET ", req.Substring(0, 4), true)) | ||
129 | _httpMethod = "GET"; | ||
130 | else if (0 == String.Compare("POST ", req.Substring(0, 5), true)) | ||
131 | _httpMethod = "POST"; | ||
132 | else | ||
133 | throw new InvalidOperationException("Unrecognized method in query: " + req); | ||
134 | |||
135 | req = req.TrimEnd(); | ||
136 | int idx = req.IndexOf(' ') + 1; | ||
137 | if (idx >= req.Length) | ||
138 | throw new ApplicationException("What do you want?"); | ||
139 | |||
140 | string page_protocol = req.Substring(idx); | ||
141 | int idx2 = page_protocol.IndexOf(' '); | ||
142 | if (idx2 == -1) | ||
143 | idx2 = page_protocol.Length; | ||
144 | |||
145 | _filePath = page_protocol.Substring(0, idx2).Trim(); | ||
146 | _protocol = page_protocol.Substring(idx2).Trim(); | ||
147 | } | ||
148 | |||
149 | private void GetRequestHeaders() | ||
150 | { | ||
151 | String line; | ||
152 | int idx; | ||
153 | |||
154 | _headers = new Hashtable(); | ||
155 | |||
156 | while ((line = _input.ReadLine()) != "") | ||
157 | { | ||
158 | if (line == null) | ||
159 | { | ||
160 | break; | ||
161 | } | ||
162 | |||
163 | idx = line.IndexOf(':'); | ||
164 | if (idx == -1 || idx == line.Length - 1) | ||
165 | { | ||
166 | Logger.WriteEntry("Malformed header line: " + line, LogLevel.Information); | ||
167 | continue; | ||
168 | } | ||
169 | |||
170 | String key = line.Substring(0, idx); | ||
171 | String value = line.Substring(idx + 1); | ||
172 | |||
173 | try | ||
174 | { | ||
175 | _headers.Add(key, value); | ||
176 | } | ||
177 | catch (Exception) | ||
178 | { | ||
179 | Logger.WriteEntry("Duplicate header key in line: " + line, LogLevel.Information); | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /// <summary> | ||
185 | /// Format the object contents into a useful string representation. | ||
186 | /// </summary> | ||
187 | ///<returns><c>String</c> representation of the <c>SimpleHttpRequest</c> as the <i>HttpMethod FilePath Protocol</i>.</returns> | ||
188 | override public String ToString() | ||
189 | { | ||
190 | return HttpMethod + " " + FilePath + " " + Protocol; | ||
191 | } | ||
192 | |||
193 | /// <summary> | ||
194 | /// Close the <c>SimpleHttpRequest</c>. This flushes and closes all associated io streams. | ||
195 | /// </summary> | ||
196 | public void Close() | ||
197 | { | ||
198 | _output.Flush(); | ||
199 | _output.Close(); | ||
200 | _input.Close(); | ||
201 | _client.Close(); | ||
202 | } | ||
203 | } | ||
204 | } | ||
diff --git a/XmlRpcCS/XMLRPC.csproj b/XmlRpcCS/XMLRPC.csproj new file mode 100644 index 0000000..2c7ef94 --- /dev/null +++ b/XmlRpcCS/XMLRPC.csproj | |||
@@ -0,0 +1,138 @@ | |||
1 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
2 | <PropertyGroup> | ||
3 | <ProjectType>Local</ProjectType> | ||
4 | <ProductVersion>8.0.50727</ProductVersion> | ||
5 | <SchemaVersion>2.0</SchemaVersion> | ||
6 | <ProjectGuid>{8E81D43C-0000-0000-0000-000000000000}</ProjectGuid> | ||
7 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
8 | <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
9 | <ApplicationIcon></ApplicationIcon> | ||
10 | <AssemblyKeyContainerName> | ||
11 | </AssemblyKeyContainerName> | ||
12 | <AssemblyName>XMLRPC</AssemblyName> | ||
13 | <DefaultClientScript>JScript</DefaultClientScript> | ||
14 | <DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout> | ||
15 | <DefaultTargetSchema>IE50</DefaultTargetSchema> | ||
16 | <DelaySign>false</DelaySign> | ||
17 | <OutputType>Library</OutputType> | ||
18 | <AppDesignerFolder></AppDesignerFolder> | ||
19 | <RootNamespace>XMLRPC</RootNamespace> | ||
20 | <StartupObject></StartupObject> | ||
21 | <FileUpgradeFlags> | ||
22 | </FileUpgradeFlags> | ||
23 | </PropertyGroup> | ||
24 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
25 | <AllowUnsafeBlocks>False</AllowUnsafeBlocks> | ||
26 | <BaseAddress>285212672</BaseAddress> | ||
27 | <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> | ||
28 | <ConfigurationOverrideFile> | ||
29 | </ConfigurationOverrideFile> | ||
30 | <DefineConstants>TRACE;DEBUG</DefineConstants> | ||
31 | <DocumentationFile></DocumentationFile> | ||
32 | <DebugSymbols>True</DebugSymbols> | ||
33 | <FileAlignment>4096</FileAlignment> | ||
34 | <Optimize>False</Optimize> | ||
35 | <OutputPath>..\bin\</OutputPath> | ||
36 | <RegisterForComInterop>False</RegisterForComInterop> | ||
37 | <RemoveIntegerChecks>False</RemoveIntegerChecks> | ||
38 | <TreatWarningsAsErrors>False</TreatWarningsAsErrors> | ||
39 | <WarningLevel>4</WarningLevel> | ||
40 | <NoWarn></NoWarn> | ||
41 | </PropertyGroup> | ||
42 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
43 | <AllowUnsafeBlocks>False</AllowUnsafeBlocks> | ||
44 | <BaseAddress>285212672</BaseAddress> | ||
45 | <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> | ||
46 | <ConfigurationOverrideFile> | ||
47 | </ConfigurationOverrideFile> | ||
48 | <DefineConstants>TRACE</DefineConstants> | ||
49 | <DocumentationFile></DocumentationFile> | ||
50 | <DebugSymbols>False</DebugSymbols> | ||
51 | <FileAlignment>4096</FileAlignment> | ||
52 | <Optimize>True</Optimize> | ||
53 | <OutputPath>..\bin\</OutputPath> | ||
54 | <RegisterForComInterop>False</RegisterForComInterop> | ||
55 | <RemoveIntegerChecks>False</RemoveIntegerChecks> | ||
56 | <TreatWarningsAsErrors>False</TreatWarningsAsErrors> | ||
57 | <WarningLevel>4</WarningLevel> | ||
58 | <NoWarn></NoWarn> | ||
59 | </PropertyGroup> | ||
60 | <ItemGroup> | ||
61 | <Reference Include="System" > | ||
62 | <HintPath>System.dll</HintPath> | ||
63 | <Private>False</Private> | ||
64 | </Reference> | ||
65 | <Reference Include="System.Xml" > | ||
66 | <HintPath>System.Xml.dll</HintPath> | ||
67 | <Private>False</Private> | ||
68 | </Reference> | ||
69 | </ItemGroup> | ||
70 | <ItemGroup> | ||
71 | </ItemGroup> | ||
72 | <ItemGroup> | ||
73 | <Compile Include="Logger.cs"> | ||
74 | <SubType>Code</SubType> | ||
75 | </Compile> | ||
76 | <Compile Include="SimpleHttpRequest.cs"> | ||
77 | <SubType>Code</SubType> | ||
78 | </Compile> | ||
79 | <Compile Include="XmlRpcBoxcarRequest.cs"> | ||
80 | <SubType>Code</SubType> | ||
81 | </Compile> | ||
82 | <Compile Include="XmlRpcClientProxy.cs"> | ||
83 | <SubType>Code</SubType> | ||
84 | </Compile> | ||
85 | <Compile Include="XmlRpcDeserializer.cs"> | ||
86 | <SubType>Code</SubType> | ||
87 | </Compile> | ||
88 | <Compile Include="XmlRpcErrorCodes.cs"> | ||
89 | <SubType>Code</SubType> | ||
90 | </Compile> | ||
91 | <Compile Include="XmlRpcException.cs"> | ||
92 | <SubType>Code</SubType> | ||
93 | </Compile> | ||
94 | <Compile Include="XmlRpcExposedAttribute.cs"> | ||
95 | <SubType>Code</SubType> | ||
96 | </Compile> | ||
97 | <Compile Include="XmlRpcRequest.cs"> | ||
98 | <SubType>Code</SubType> | ||
99 | </Compile> | ||
100 | <Compile Include="XmlRpcRequestDeserializer.cs"> | ||
101 | <SubType>Code</SubType> | ||
102 | </Compile> | ||
103 | <Compile Include="XmlRpcRequestSerializer.cs"> | ||
104 | <SubType>Code</SubType> | ||
105 | </Compile> | ||
106 | <Compile Include="XmlRpcResponder.cs"> | ||
107 | <SubType>Code</SubType> | ||
108 | </Compile> | ||
109 | <Compile Include="XmlRpcResponse.cs"> | ||
110 | <SubType>Code</SubType> | ||
111 | </Compile> | ||
112 | <Compile Include="XmlRpcResponseDeserializer.cs"> | ||
113 | <SubType>Code</SubType> | ||
114 | </Compile> | ||
115 | <Compile Include="XmlRpcResponseSerializer.cs"> | ||
116 | <SubType>Code</SubType> | ||
117 | </Compile> | ||
118 | <Compile Include="XmlRpcSerializer.cs"> | ||
119 | <SubType>Code</SubType> | ||
120 | </Compile> | ||
121 | <Compile Include="XmlRpcServer.cs"> | ||
122 | <SubType>Code</SubType> | ||
123 | </Compile> | ||
124 | <Compile Include="XmlRpcSystemObject.cs"> | ||
125 | <SubType>Code</SubType> | ||
126 | </Compile> | ||
127 | <Compile Include="XmlRpcXmlTokens.cs"> | ||
128 | <SubType>Code</SubType> | ||
129 | </Compile> | ||
130 | </ItemGroup> | ||
131 | <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /> | ||
132 | <PropertyGroup> | ||
133 | <PreBuildEvent> | ||
134 | </PreBuildEvent> | ||
135 | <PostBuildEvent> | ||
136 | </PostBuildEvent> | ||
137 | </PropertyGroup> | ||
138 | </Project> | ||
diff --git a/XmlRpcCS/XMLRPC.dll.build b/XmlRpcCS/XMLRPC.dll.build new file mode 100644 index 0000000..d734d19 --- /dev/null +++ b/XmlRpcCS/XMLRPC.dll.build | |||
@@ -0,0 +1,58 @@ | |||
1 | <?xml version="1.0" ?> | ||
2 | <project name="XMLRPC" default="build"> | ||
3 | <target name="build"> | ||
4 | <echo message="Build Directory is ${project::get-base-directory()}/${build.dir}" /> | ||
5 | <mkdir dir="${project::get-base-directory()}/${build.dir}" /> | ||
6 | <copy todir="${project::get-base-directory()}/${build.dir}"> | ||
7 | <fileset basedir="${project::get-base-directory()}"> | ||
8 | </fileset> | ||
9 | </copy> | ||
10 | <csc target="library" debug="${build.debug}" unsafe="False" define="TRACE;DEBUG" output="${project::get-base-directory()}/${build.dir}/${project::get-name()}.dll"> | ||
11 | <resources prefix="XMLRPC" dynamicprefix="true" > | ||
12 | </resources> | ||
13 | <sources failonempty="true"> | ||
14 | <include name="Logger.cs" /> | ||
15 | <include name="SimpleHttpRequest.cs" /> | ||
16 | <include name="XmlRpcBoxcarRequest.cs" /> | ||
17 | <include name="XmlRpcClientProxy.cs" /> | ||
18 | <include name="XmlRpcDeserializer.cs" /> | ||
19 | <include name="XmlRpcErrorCodes.cs" /> | ||
20 | <include name="XmlRpcException.cs" /> | ||
21 | <include name="XmlRpcExposedAttribute.cs" /> | ||
22 | <include name="XmlRpcRequest.cs" /> | ||
23 | <include name="XmlRpcRequestDeserializer.cs" /> | ||
24 | <include name="XmlRpcRequestSerializer.cs" /> | ||
25 | <include name="XmlRpcResponder.cs" /> | ||
26 | <include name="XmlRpcResponse.cs" /> | ||
27 | <include name="XmlRpcResponseDeserializer.cs" /> | ||
28 | <include name="XmlRpcResponseSerializer.cs" /> | ||
29 | <include name="XmlRpcSerializer.cs" /> | ||
30 | <include name="XmlRpcServer.cs" /> | ||
31 | <include name="XmlRpcSystemObject.cs" /> | ||
32 | <include name="XmlRpcXmlTokens.cs" /> | ||
33 | </sources> | ||
34 | <references basedir="${project::get-base-directory()}"> | ||
35 | <lib> | ||
36 | <include name="${project::get-base-directory()}" /> | ||
37 | <include name="${project::get-base-directory()}/${build.dir}" /> | ||
38 | </lib> | ||
39 | <include name="System.dll" /> | ||
40 | <include name="System.Xml.dll" /> | ||
41 | </references> | ||
42 | </csc> | ||
43 | <echo message="Copying from [${project::get-base-directory()}/${build.dir}/] to [${project::get-base-directory()}/../bin/" /> | ||
44 | <mkdir dir="${project::get-base-directory()}/../bin/"/> | ||
45 | <copy todir="${project::get-base-directory()}/../bin/"> | ||
46 | <fileset basedir="${project::get-base-directory()}/${build.dir}/" > | ||
47 | <include name="*.dll"/> | ||
48 | <include name="*.exe"/> | ||
49 | </fileset> | ||
50 | </copy> | ||
51 | </target> | ||
52 | <target name="clean"> | ||
53 | <delete dir="${bin.dir}" failonerror="false" /> | ||
54 | <delete dir="${obj.dir}" failonerror="false" /> | ||
55 | </target> | ||
56 | <target name="doc" description="Creates documentation."> | ||
57 | </target> | ||
58 | </project> | ||
diff --git a/XmlRpcCS/XmlRpcBoxcarRequest.cs b/XmlRpcCS/XmlRpcBoxcarRequest.cs new file mode 100644 index 0000000..f87f7a5 --- /dev/null +++ b/XmlRpcCS/XmlRpcBoxcarRequest.cs | |||
@@ -0,0 +1,51 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | using System.Net; | ||
8 | using System.Text; | ||
9 | using System.Reflection; | ||
10 | |||
11 | /// <summary>Class that collects individual <c>XmlRpcRequest</c> objects and submits them as a <i>boxcarred</i> request.</summary> | ||
12 | /// <remarks>A boxcared request is when a number of request are collected before being sent via XML-RPC, and then are sent via | ||
13 | /// a single HTTP connection. This results in a speed up from reduced connection time. The results are then retuned collectively | ||
14 | /// as well. | ||
15 | ///</remarks> | ||
16 | /// <seealso cref="XmlRpcRequest"/> | ||
17 | public class XmlRpcBoxcarRequest : XmlRpcRequest | ||
18 | { | ||
19 | /// <summary>ArrayList to collect the requests to boxcar.</summary> | ||
20 | public IList Requests = new ArrayList(); | ||
21 | |||
22 | /// <summary>Basic constructor.</summary> | ||
23 | public XmlRpcBoxcarRequest() | ||
24 | { | ||
25 | } | ||
26 | |||
27 | /// <summary>Returns the <c>String</c> "system.multiCall" which is the server method that handles boxcars.</summary> | ||
28 | public override String MethodName | ||
29 | { | ||
30 | get { return "system.multiCall"; } | ||
31 | } | ||
32 | |||
33 | /// <summary>The <c>ArrayList</c> of boxcarred <paramref>Requests</paramref> as properly formed parameters.</summary> | ||
34 | public override IList Params | ||
35 | { | ||
36 | get { | ||
37 | _params.Clear(); | ||
38 | ArrayList reqArray = new ArrayList(); | ||
39 | foreach (XmlRpcRequest request in Requests) | ||
40 | { | ||
41 | Hashtable requestEntry = new Hashtable(); | ||
42 | requestEntry.Add(XmlRpcXmlTokens.METHOD_NAME, request.MethodName); | ||
43 | requestEntry.Add(XmlRpcXmlTokens.PARAMS, request.Params); | ||
44 | reqArray.Add(requestEntry); | ||
45 | } | ||
46 | _params.Add(reqArray); | ||
47 | return _params; | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
diff --git a/XmlRpcCS/XmlRpcClientProxy.cs b/XmlRpcCS/XmlRpcClientProxy.cs new file mode 100644 index 0000000..f52273a --- /dev/null +++ b/XmlRpcCS/XmlRpcClientProxy.cs | |||
@@ -0,0 +1,61 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Runtime.Remoting.Proxies; | ||
5 | using System.Runtime.Remoting.Messaging; | ||
6 | |||
7 | /// <summary>This class provides support for creating local proxies of XML-RPC remote objects</summary> | ||
8 | /// <remarks> | ||
9 | /// To create a local proxy you need to create a local C# interface and then, via <i>createProxy</i> | ||
10 | /// associate that interface with a remote object at a given URL. | ||
11 | /// </remarks> | ||
12 | public class XmlRpcClientProxy : RealProxy | ||
13 | { | ||
14 | private String _remoteObjectName; | ||
15 | private String _url; | ||
16 | private XmlRpcRequest _client = new XmlRpcRequest(); | ||
17 | |||
18 | /// <summary>Factory method to create proxies.</summary> | ||
19 | /// <remarks> | ||
20 | /// To create a local proxy you need to create a local C# interface with methods that mirror those of the server object. | ||
21 | /// Next, pass that interface into <c>createProxy</c> along with the object name and URL of the remote object and | ||
22 | /// cast the resulting object to the specifice interface. | ||
23 | /// </remarks> | ||
24 | /// <param name="remoteObjectName"><c>String</c> The name of the remote object.</param> | ||
25 | /// <param name="url"><c>String</c> The URL of the remote object.</param> | ||
26 | /// <param name="anInterface"><c>Type</c> The typeof() of a C# interface.</param> | ||
27 | /// <returns><c>Object</c> A proxy for your specified interface. Cast to appropriate type.</returns> | ||
28 | public static Object createProxy(String remoteObjectName, String url, Type anInterface) | ||
29 | { | ||
30 | return new XmlRpcClientProxy(remoteObjectName, url, anInterface).GetTransparentProxy(); | ||
31 | } | ||
32 | |||
33 | private XmlRpcClientProxy(String remoteObjectName, String url, Type t) : base(t) | ||
34 | { | ||
35 | _remoteObjectName = remoteObjectName; | ||
36 | _url = url; | ||
37 | } | ||
38 | |||
39 | /// <summary>The local method dispatcher - do not invoke.</summary> | ||
40 | override public IMessage Invoke(IMessage msg) | ||
41 | { | ||
42 | IMethodCallMessage methodMessage = (IMethodCallMessage)msg; | ||
43 | |||
44 | _client.MethodName = _remoteObjectName + "." + methodMessage.MethodName; | ||
45 | _client.Params.Clear(); | ||
46 | foreach (Object o in methodMessage.Args) | ||
47 | _client.Params.Add(o); | ||
48 | |||
49 | try | ||
50 | { | ||
51 | Object ret = _client.Invoke(_url); | ||
52 | return new ReturnMessage(ret,null,0, | ||
53 | methodMessage.LogicalCallContext, methodMessage); | ||
54 | } | ||
55 | catch (Exception e) | ||
56 | { | ||
57 | return new ReturnMessage(e, methodMessage); | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/XmlRpcCS/XmlRpcDeserializer.cs b/XmlRpcCS/XmlRpcDeserializer.cs new file mode 100644 index 0000000..bd736c0 --- /dev/null +++ b/XmlRpcCS/XmlRpcDeserializer.cs | |||
@@ -0,0 +1,195 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | using System.Globalization; | ||
8 | |||
9 | /// <summary>Parser context, we maintain contexts in a stack to avoiding recursion. </summary> | ||
10 | struct Context | ||
11 | { | ||
12 | public String Name; | ||
13 | public Object Container; | ||
14 | } | ||
15 | |||
16 | /// <summary>Basic XML-RPC data deserializer.</summary> | ||
17 | /// <remarks>Uses <c>XmlTextReader</c> to parse the XML data. This level of the class | ||
18 | /// only handles the tokens common to both Requests and Responses. This class is not useful in and of itself | ||
19 | /// but is designed to be subclassed.</remarks> | ||
20 | public class XmlRpcDeserializer : XmlRpcXmlTokens | ||
21 | { | ||
22 | private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo(); | ||
23 | |||
24 | private Object _container; | ||
25 | private Stack _containerStack; | ||
26 | |||
27 | /// <summary>Protected reference to last text.</summary> | ||
28 | protected String _text; | ||
29 | /// <summary>Protected reference to last deserialized value.</summary> | ||
30 | protected Object _value; | ||
31 | /// <summary>Protected reference to last name field.</summary> | ||
32 | protected String _name; | ||
33 | |||
34 | |||
35 | /// <summary>Basic constructor.</summary> | ||
36 | public XmlRpcDeserializer() | ||
37 | { | ||
38 | Reset(); | ||
39 | _dateFormat.FullDateTimePattern = ISO_DATETIME; | ||
40 | } | ||
41 | |||
42 | /// <summary>Static method that parses XML data into a response using the Singleton.</summary> | ||
43 | /// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC response.</param> | ||
44 | /// <returns><c>Object</c> object resulting from the deserialization.</returns> | ||
45 | virtual public Object Deserialize(TextReader xmlData) | ||
46 | { | ||
47 | return null; | ||
48 | } | ||
49 | |||
50 | /// <summary>Protected method to parse a node in an XML-RPC XML stream.</summary> | ||
51 | /// <remarks>Method deals with elements common to all XML-RPC data, subclasses of | ||
52 | /// this object deal with request/response spefic elements.</remarks> | ||
53 | /// <param name="reader"><c>XmlTextReader</c> of the in progress parsing data stream.</param> | ||
54 | protected void DeserializeNode(XmlTextReader reader) | ||
55 | { | ||
56 | switch (reader.NodeType) | ||
57 | { | ||
58 | case XmlNodeType.Element: | ||
59 | if (Logger.Delegate != null) | ||
60 | Logger.WriteEntry("START " + reader.Name, LogLevel.Information); | ||
61 | switch (reader.Name) | ||
62 | { | ||
63 | case VALUE: | ||
64 | _value = null; | ||
65 | _text = null; | ||
66 | break; | ||
67 | case STRUCT: | ||
68 | PushContext(); | ||
69 | _container = new Hashtable(); | ||
70 | break; | ||
71 | case ARRAY: | ||
72 | PushContext(); | ||
73 | _container = new ArrayList(); | ||
74 | break; | ||
75 | } | ||
76 | break; | ||
77 | case XmlNodeType.EndElement: | ||
78 | if (Logger.Delegate != null) | ||
79 | Logger.WriteEntry("END " + reader.Name, LogLevel.Information); | ||
80 | switch (reader.Name) | ||
81 | { | ||
82 | case BASE64: | ||
83 | _value = Convert.FromBase64String(_text); | ||
84 | break; | ||
85 | case BOOLEAN: | ||
86 | int val = Int16.Parse(_text); | ||
87 | if (val == 0) | ||
88 | _value = false; | ||
89 | else if (val == 1) | ||
90 | _value = true; | ||
91 | break; | ||
92 | case STRING: | ||
93 | _value = _text; | ||
94 | break; | ||
95 | case DOUBLE: | ||
96 | _value = Double.Parse(_text); | ||
97 | break; | ||
98 | case INT: | ||
99 | case ALT_INT: | ||
100 | _value = Int32.Parse(_text); | ||
101 | break; | ||
102 | case DATETIME: | ||
103 | #if __MONO__ | ||
104 | _value = DateParse(_text); | ||
105 | #else | ||
106 | _value = DateTime.ParseExact(_text, "F", _dateFormat); | ||
107 | #endif | ||
108 | break; | ||
109 | case NAME: | ||
110 | _name = _text; | ||
111 | break; | ||
112 | case VALUE: | ||
113 | if (_value == null) | ||
114 | _value = _text; // some kits don't use <string> tag, they just do <value> | ||
115 | |||
116 | if ((_container != null) && (_container is IList)) // in an array? If so add value to it. | ||
117 | ((IList)_container).Add(_value); | ||
118 | break; | ||
119 | case MEMBER: | ||
120 | if ((_container != null) && (_container is IDictionary)) // in an struct? If so add value to it. | ||
121 | ((IDictionary)_container).Add(_name, _value); | ||
122 | break; | ||
123 | case ARRAY: | ||
124 | case STRUCT: | ||
125 | _value = _container; | ||
126 | PopContext(); | ||
127 | break; | ||
128 | } | ||
129 | break; | ||
130 | case XmlNodeType.Text: | ||
131 | if (Logger.Delegate != null) | ||
132 | Logger.WriteEntry("Text " + reader.Value, LogLevel.Information); | ||
133 | _text = reader.Value; | ||
134 | break; | ||
135 | default: | ||
136 | break; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /// <summary>Static method that parses XML in a <c>String</c> into a | ||
141 | /// request using the Singleton.</summary> | ||
142 | /// <param name="xmlData"><c>String</c> containing an XML-RPC request.</param> | ||
143 | /// <returns><c>XmlRpcRequest</c> object resulting from the parse.</returns> | ||
144 | public Object Deserialize(String xmlData) | ||
145 | { | ||
146 | StringReader sr = new StringReader(xmlData); | ||
147 | return Deserialize(sr); | ||
148 | } | ||
149 | |||
150 | /// <summary>Pop a Context of the stack, an Array or Struct has closed.</summary> | ||
151 | private void PopContext() | ||
152 | { | ||
153 | Context c = (Context)_containerStack.Pop(); | ||
154 | _container = c.Container; | ||
155 | _name = c.Name; | ||
156 | } | ||
157 | |||
158 | /// <summary>Push a Context on the stack, an Array or Struct has opened.</summary> | ||
159 | private void PushContext() | ||
160 | { | ||
161 | Context context; | ||
162 | |||
163 | context.Container = _container; | ||
164 | context.Name = _name; | ||
165 | |||
166 | _containerStack.Push(context); | ||
167 | } | ||
168 | |||
169 | /// <summary>Reset the internal state of the deserializer.</summary> | ||
170 | protected void Reset() | ||
171 | { | ||
172 | _text = null; | ||
173 | _value = null; | ||
174 | _name = null; | ||
175 | _container = null; | ||
176 | _containerStack = new Stack(); | ||
177 | } | ||
178 | |||
179 | #if __MONO__ | ||
180 | private DateTime DateParse(String str) | ||
181 | { | ||
182 | int year = Int32.Parse(str.Substring(0,4)); | ||
183 | int month = Int32.Parse(str.Substring(4,2)); | ||
184 | int day = Int32.Parse(str.Substring(6,2)); | ||
185 | int hour = Int32.Parse(str.Substring(9,2)); | ||
186 | int min = Int32.Parse(str.Substring(12,2)); | ||
187 | int sec = Int32.Parse(str.Substring(15,2)); | ||
188 | return new DateTime(year,month,day,hour,min,sec); | ||
189 | } | ||
190 | #endif | ||
191 | |||
192 | } | ||
193 | } | ||
194 | |||
195 | |||
diff --git a/XmlRpcCS/XmlRpcErrorCodes.cs b/XmlRpcCS/XmlRpcErrorCodes.cs new file mode 100644 index 0000000..6dec57d --- /dev/null +++ b/XmlRpcCS/XmlRpcErrorCodes.cs | |||
@@ -0,0 +1,51 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | |||
5 | /// <summary>Standard XML-RPC error codes.</summary> | ||
6 | public class XmlRpcErrorCodes | ||
7 | { | ||
8 | /// <summary></summary> | ||
9 | public const int PARSE_ERROR_MALFORMED = -32700; | ||
10 | /// <summary></summary> | ||
11 | public const String PARSE_ERROR_MALFORMED_MSG = "Parse Error, not well formed"; | ||
12 | |||
13 | /// <summary></summary> | ||
14 | public const int PARSE_ERROR_ENCODING = -32701; | ||
15 | /// <summary></summary> | ||
16 | public const String PARSE_ERROR_ENCODING_MSG = "Parse Error, unsupported encoding"; | ||
17 | |||
18 | // | ||
19 | // -32702 ---> parse error. invalid character for encoding | ||
20 | // -32600 ---> server error. invalid xml-rpc. not conforming to spec. | ||
21 | // | ||
22 | |||
23 | /// <summary></summary> | ||
24 | public const int SERVER_ERROR_METHOD = -32601; | ||
25 | /// <summary></summary> | ||
26 | public const String SERVER_ERROR_METHOD_MSG = "Server Error, requested method not found"; | ||
27 | |||
28 | /// <summary></summary> | ||
29 | public const int SERVER_ERROR_PARAMS = -32602; | ||
30 | /// <summary></summary> | ||
31 | public const String SERVER_ERROR_PARAMS_MSG = "Server Error, invalid method parameters"; | ||
32 | |||
33 | // | ||
34 | // -32603 ---> server error. internal xml-rpc error | ||
35 | // | ||
36 | |||
37 | /// <summary></summary> | ||
38 | public const int APPLICATION_ERROR = -32500; | ||
39 | /// <summary></summary> | ||
40 | public const String APPLICATION_ERROR_MSG = "Application Error"; | ||
41 | |||
42 | // | ||
43 | // -32400 ---> system error | ||
44 | // | ||
45 | |||
46 | /// <summary></summary> | ||
47 | public const int TRANSPORT_ERROR = -32300; | ||
48 | /// <summary></summary> | ||
49 | public const String TRANSPORT_ERROR_MSG = "Transport Layer Error"; | ||
50 | } | ||
51 | } | ||
diff --git a/XmlRpcCS/XmlRpcException.cs b/XmlRpcCS/XmlRpcException.cs new file mode 100644 index 0000000..fd1f4ae --- /dev/null +++ b/XmlRpcCS/XmlRpcException.cs | |||
@@ -0,0 +1,39 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | |||
5 | /// <summary>An XML-RPC Exception.</summary> | ||
6 | /// <remarks>Maps a C# exception to an XML-RPC fault. Normal exceptions | ||
7 | /// include a message so this adds the code needed by XML-RPC.</remarks> | ||
8 | public class XmlRpcException : Exception | ||
9 | { | ||
10 | private int _code; | ||
11 | |||
12 | /// <summary>Instantiate an <c>XmlRpcException</c> with a code and message.</summary> | ||
13 | /// <param name="code"><c>Int</c> faultCode associated with this exception.</param> | ||
14 | /// <param name="message"><c>String</c> faultMessage associated with this exception.</param> | ||
15 | public XmlRpcException(int code, String message) | ||
16 | : base(message) | ||
17 | { | ||
18 | _code = code; | ||
19 | } | ||
20 | |||
21 | /// <summary>The value of the faults message, i.e. the faultString.</summary> | ||
22 | public String FaultString | ||
23 | { | ||
24 | get { return Message; } | ||
25 | } | ||
26 | |||
27 | /// <summary>The value of the faults code, i.e. the faultCode.</summary> | ||
28 | public int FaultCode | ||
29 | { | ||
30 | get { return _code; } | ||
31 | } | ||
32 | |||
33 | /// <summary>Format the message to include the code.</summary> | ||
34 | override public String ToString() | ||
35 | { | ||
36 | return "Code: " + FaultCode + " Message: " + base.ToString(); | ||
37 | } | ||
38 | } | ||
39 | } | ||
diff --git a/XmlRpcCS/XmlRpcExposedAttribute.cs b/XmlRpcCS/XmlRpcExposedAttribute.cs new file mode 100644 index 0000000..67b27ae --- /dev/null +++ b/XmlRpcCS/XmlRpcExposedAttribute.cs | |||
@@ -0,0 +1,60 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Reflection; | ||
5 | |||
6 | /// <summary> | ||
7 | /// Simple tagging attribute to indicate participation is XML-RPC exposure. | ||
8 | /// </summary> | ||
9 | /// <remarks> | ||
10 | /// If present at the class level it indicates that this class does explicitly | ||
11 | /// expose methods. If present at the method level it denotes that the method | ||
12 | /// is exposed. | ||
13 | /// </remarks> | ||
14 | [AttributeUsage( | ||
15 | AttributeTargets.Class | AttributeTargets.Method, | ||
16 | AllowMultiple = false, | ||
17 | Inherited = true | ||
18 | )] | ||
19 | public class XmlRpcExposedAttribute : Attribute | ||
20 | { | ||
21 | /// <summary>Check if <paramref>obj</paramref> is an object utilizing the XML-RPC exposed Attribute.</summary> | ||
22 | /// <param name="obj"><c>Object</c> of a class or method to check for attribute.</param> | ||
23 | /// <returns><c>Boolean</c> true if attribute present.</returns> | ||
24 | public static Boolean ExposedObject(Object obj) | ||
25 | { | ||
26 | return IsExposed(obj.GetType()); | ||
27 | } | ||
28 | |||
29 | /// <summary>Check if <paramref>obj</paramref>.<paramref>methodName</paramref> is an XML-RPC exposed method.</summary> | ||
30 | /// <remarks>A method is considered to be exposed if it exists and, either, the object does not use the XmlRpcExposed attribute, | ||
31 | /// or the object does use the XmlRpcExposed attribute and the method has the XmlRpcExposed attribute as well.</remarks> | ||
32 | /// <returns><c>Boolean</c> true if the method is exposed.</returns> | ||
33 | public static Boolean ExposedMethod(Object obj, String methodName) | ||
34 | { | ||
35 | Type type = obj.GetType(); | ||
36 | MethodInfo method = type.GetMethod(methodName); | ||
37 | |||
38 | if (method == null) | ||
39 | throw new MissingMethodException("Method " + methodName + " not found."); | ||
40 | |||
41 | if (!IsExposed(type)) | ||
42 | return true; | ||
43 | |||
44 | return IsExposed(method); | ||
45 | } | ||
46 | |||
47 | /// <summary>Check if <paramref>mi</paramref> is XML-RPC exposed.</summary> | ||
48 | /// <param name="mi"><c>MemberInfo</c> of a class or method to check for attribute.</param> | ||
49 | /// <returns><c>Boolean</c> true if attribute present.</returns> | ||
50 | public static Boolean IsExposed(MemberInfo mi) | ||
51 | { | ||
52 | foreach (Attribute attr in mi.GetCustomAttributes(true)) | ||
53 | { | ||
54 | if (attr is XmlRpcExposedAttribute) | ||
55 | return true; | ||
56 | } | ||
57 | return false; | ||
58 | } | ||
59 | } | ||
60 | } | ||
diff --git a/XmlRpcCS/XmlRpcRequest.cs b/XmlRpcCS/XmlRpcRequest.cs new file mode 100644 index 0000000..18d2182 --- /dev/null +++ b/XmlRpcCS/XmlRpcRequest.cs | |||
@@ -0,0 +1,150 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | using System.Net; | ||
8 | using System.Text; | ||
9 | using System.Reflection; | ||
10 | using System.Net.Security; | ||
11 | using System.Security.Cryptography.X509Certificates; | ||
12 | |||
13 | internal class AcceptAllCertificatePolicy : ICertificatePolicy | ||
14 | { | ||
15 | public AcceptAllCertificatePolicy() | ||
16 | { | ||
17 | } | ||
18 | |||
19 | public bool CheckValidationResult(ServicePoint sPoint, | ||
20 | System.Security.Cryptography.X509Certificates.X509Certificate cert, | ||
21 | WebRequest wRequest, int certProb) | ||
22 | { | ||
23 | // Always accept | ||
24 | return true; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | /// <summary>Class supporting the request side of an XML-RPC transaction.</summary> | ||
29 | public class XmlRpcRequest | ||
30 | { | ||
31 | private String _methodName = null; | ||
32 | private Encoding _encoding = new ASCIIEncoding(); | ||
33 | private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer(); | ||
34 | private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer(); | ||
35 | |||
36 | /// <summary><c>ArrayList</c> containing the parameters.</summary> | ||
37 | protected IList _params = null; | ||
38 | |||
39 | /// <summary>Instantiate an <c>XmlRpcRequest</c></summary> | ||
40 | public XmlRpcRequest() | ||
41 | { | ||
42 | _params = new ArrayList(); | ||
43 | } | ||
44 | |||
45 | /// <summary>Instantiate an <c>XmlRpcRequest</c> for a specified method and parameters.</summary> | ||
46 | /// <param name="methodName"><c>String</c> designating the <i>object.method</i> on the server the request | ||
47 | /// should be directed to.</param> | ||
48 | /// <param name="parameters"><c>ArrayList</c> of XML-RPC type parameters to invoke the request with.</param> | ||
49 | public XmlRpcRequest(String methodName, IList parameters) | ||
50 | { | ||
51 | MethodName = methodName; | ||
52 | _params = parameters; | ||
53 | } | ||
54 | |||
55 | /// <summary><c>ArrayList</c> conntaining the parameters for the request.</summary> | ||
56 | public virtual IList Params | ||
57 | { | ||
58 | get { return _params; } | ||
59 | } | ||
60 | |||
61 | /// <summary><c>String</c> conntaining the method name, both object and method, that the request will be sent to.</summary> | ||
62 | public virtual String MethodName | ||
63 | { | ||
64 | get { return _methodName; } | ||
65 | set { _methodName = value; } | ||
66 | } | ||
67 | |||
68 | /// <summary><c>String</c> object name portion of the method name.</summary> | ||
69 | public String MethodNameObject | ||
70 | { | ||
71 | get | ||
72 | { | ||
73 | int index = MethodName.IndexOf("."); | ||
74 | |||
75 | if (index == -1) | ||
76 | return MethodName; | ||
77 | |||
78 | return MethodName.Substring(0, index); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | /// <summary><c>String</c> method name portion of the object.method name.</summary> | ||
83 | public String MethodNameMethod | ||
84 | { | ||
85 | get | ||
86 | { | ||
87 | int index = MethodName.IndexOf("."); | ||
88 | |||
89 | if (index == -1) | ||
90 | return MethodName; | ||
91 | |||
92 | return MethodName.Substring(index + 1, MethodName.Length - index - 1); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /// <summary>Invoke this request on the server.</summary> | ||
97 | /// <param name="url"><c>String</c> The url of the XML-RPC server.</param> | ||
98 | /// <returns><c>Object</c> The value returned from the method invocation on the server.</returns> | ||
99 | /// <exception cref="XmlRpcException">If an exception generated on the server side.</exception> | ||
100 | public Object Invoke(String url) | ||
101 | { | ||
102 | XmlRpcResponse res = Send(url, 10000); | ||
103 | |||
104 | if (res.IsFault) | ||
105 | throw new XmlRpcException(res.FaultCode, res.FaultString); | ||
106 | |||
107 | return res.Value; | ||
108 | } | ||
109 | |||
110 | /// <summary>Send the request to the server.</summary> | ||
111 | /// <param name="url"><c>String</c> The url of the XML-RPC server.</param> | ||
112 | /// <param name="timeout">Milliseconds before the connection times out.</param> | ||
113 | /// <returns><c>XmlRpcResponse</c> The response generated.</returns> | ||
114 | public XmlRpcResponse Send(String url, int timeout) | ||
115 | { | ||
116 | // Override SSL authentication mechanisms | ||
117 | ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy(); | ||
118 | |||
119 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); | ||
120 | if (request == null) | ||
121 | throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR, | ||
122 | XmlRpcErrorCodes.TRANSPORT_ERROR_MSG + ": Could not create request with " + url); | ||
123 | request.Method = "POST"; | ||
124 | request.ContentType = "text/xml"; | ||
125 | request.AllowWriteStreamBuffering = true; | ||
126 | request.Timeout = timeout; | ||
127 | |||
128 | Stream stream = request.GetRequestStream(); | ||
129 | XmlTextWriter xml = new XmlTextWriter(stream, _encoding); | ||
130 | _serializer.Serialize(xml, this); | ||
131 | xml.Flush(); | ||
132 | xml.Close(); | ||
133 | |||
134 | HttpWebResponse response = (HttpWebResponse)request.GetResponse(); | ||
135 | StreamReader input = new StreamReader(response.GetResponseStream()); | ||
136 | |||
137 | XmlRpcResponse resp = (XmlRpcResponse)_deserializer.Deserialize(input); | ||
138 | input.Close(); | ||
139 | response.Close(); | ||
140 | return resp; | ||
141 | } | ||
142 | |||
143 | /// <summary>Produce <c>String</c> representation of the object.</summary> | ||
144 | /// <returns><c>String</c> representation of the object.</returns> | ||
145 | override public String ToString() | ||
146 | { | ||
147 | return _serializer.Serialize(this); | ||
148 | } | ||
149 | } | ||
150 | } | ||
diff --git a/XmlRpcCS/XmlRpcRequestDeserializer.cs b/XmlRpcCS/XmlRpcRequestDeserializer.cs new file mode 100644 index 0000000..0770b7e --- /dev/null +++ b/XmlRpcCS/XmlRpcRequestDeserializer.cs | |||
@@ -0,0 +1,64 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.Diagnostics; | ||
6 | using System.IO; | ||
7 | using System.Xml; | ||
8 | |||
9 | /// <summary>Class to deserialize XML data representing a request.</summary> | ||
10 | public class XmlRpcRequestDeserializer : XmlRpcDeserializer | ||
11 | { | ||
12 | static private XmlRpcRequestDeserializer _singleton; | ||
13 | /// <summary>A static singleton instance of this deserializer.</summary> | ||
14 | [Obsolete("This object is now thread safe, just use an instance.", false)] | ||
15 | static public XmlRpcRequestDeserializer Singleton | ||
16 | { | ||
17 | get | ||
18 | { | ||
19 | if (_singleton == null) | ||
20 | _singleton = new XmlRpcRequestDeserializer(); | ||
21 | |||
22 | return _singleton; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | /// <summary>Static method that parses XML data into a request using the Singleton.</summary> | ||
27 | /// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC request.</param> | ||
28 | /// <returns><c>XmlRpcRequest</c> object resulting from the parse.</returns> | ||
29 | override public Object Deserialize(TextReader xmlData) | ||
30 | { | ||
31 | XmlTextReader reader = new XmlTextReader(xmlData); | ||
32 | XmlRpcRequest request = new XmlRpcRequest(); | ||
33 | bool done = false; | ||
34 | |||
35 | lock (this) | ||
36 | { | ||
37 | Reset(); | ||
38 | while (!done && reader.Read()) | ||
39 | { | ||
40 | DeserializeNode(reader); // Parent parse... | ||
41 | switch (reader.NodeType) | ||
42 | { | ||
43 | case XmlNodeType.EndElement: | ||
44 | switch (reader.Name) | ||
45 | { | ||
46 | case METHOD_NAME: | ||
47 | request.MethodName = _text; | ||
48 | break; | ||
49 | case METHOD_CALL: | ||
50 | done = true; | ||
51 | break; | ||
52 | case PARAM: | ||
53 | request.Params.Add(_value); | ||
54 | _text = null; | ||
55 | break; | ||
56 | } | ||
57 | break; | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | return request; | ||
62 | } | ||
63 | } | ||
64 | } | ||
diff --git a/XmlRpcCS/XmlRpcRequestSerializer.cs b/XmlRpcCS/XmlRpcRequestSerializer.cs new file mode 100644 index 0000000..8099bdb --- /dev/null +++ b/XmlRpcCS/XmlRpcRequestSerializer.cs | |||
@@ -0,0 +1,51 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.Xml; | ||
6 | using System.IO; | ||
7 | |||
8 | /// <summary>Class responsible for serializing an XML-RPC request.</summary> | ||
9 | /// <remarks>This class handles the request envelope, depending on <c>XmlRpcSerializer</c> | ||
10 | /// to serialize the payload.</remarks> | ||
11 | /// <seealso cref="XmlRpcSerializer"/> | ||
12 | public class XmlRpcRequestSerializer : XmlRpcSerializer | ||
13 | { | ||
14 | static private XmlRpcRequestSerializer _singleton; | ||
15 | /// <summary>A static singleton instance of this deserializer.</summary> | ||
16 | static public XmlRpcRequestSerializer Singleton | ||
17 | { | ||
18 | get | ||
19 | { | ||
20 | if (_singleton == null) | ||
21 | _singleton = new XmlRpcRequestSerializer(); | ||
22 | |||
23 | return _singleton; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | /// <summary>Serialize the <c>XmlRpcRequest</c> to the output stream.</summary> | ||
28 | /// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param> | ||
29 | /// <param name="obj">An <c>XmlRpcRequest</c> to serialize.</param> | ||
30 | /// <seealso cref="XmlRpcRequest"/> | ||
31 | override public void Serialize(XmlTextWriter output, Object obj) | ||
32 | { | ||
33 | XmlRpcRequest request = (XmlRpcRequest)obj; | ||
34 | output.WriteStartDocument(); | ||
35 | output.WriteStartElement(METHOD_CALL); | ||
36 | output.WriteElementString(METHOD_NAME, request.MethodName); | ||
37 | output.WriteStartElement(PARAMS); | ||
38 | foreach (Object param in request.Params) | ||
39 | { | ||
40 | output.WriteStartElement(PARAM); | ||
41 | output.WriteStartElement(VALUE); | ||
42 | SerializeObject(output, param); | ||
43 | output.WriteEndElement(); | ||
44 | output.WriteEndElement(); | ||
45 | } | ||
46 | |||
47 | output.WriteEndElement(); | ||
48 | output.WriteEndElement(); | ||
49 | } | ||
50 | } | ||
51 | } | ||
diff --git a/XmlRpcCS/XmlRpcResponder.cs b/XmlRpcCS/XmlRpcResponder.cs new file mode 100644 index 0000000..0412568 --- /dev/null +++ b/XmlRpcCS/XmlRpcResponder.cs | |||
@@ -0,0 +1,98 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Xml; | ||
5 | using System.Net.Sockets; | ||
6 | |||
7 | /// <summary>The class is a container of the context of an XML-RPC dialog on the server side.</summary> | ||
8 | /// <remarks>Instances of this class maintain the context for an individual XML-RPC server | ||
9 | /// side dialog. Namely they manage an inbound deserializer and an outbound serializer. </remarks> | ||
10 | public class XmlRpcResponder | ||
11 | { | ||
12 | private XmlRpcRequestDeserializer _deserializer = new XmlRpcRequestDeserializer(); | ||
13 | private XmlRpcResponseSerializer _serializer = new XmlRpcResponseSerializer(); | ||
14 | private XmlRpcServer _server; | ||
15 | private TcpClient _client; | ||
16 | private SimpleHttpRequest _httpReq; | ||
17 | |||
18 | /// <summary>The SimpleHttpRequest based on the TcpClient.</summary> | ||
19 | public SimpleHttpRequest HttpReq | ||
20 | { | ||
21 | get { return _httpReq; } | ||
22 | } | ||
23 | |||
24 | /// <summary>Basic constructor.</summary> | ||
25 | /// <param name="server">XmlRpcServer that this XmlRpcResponder services.</param> | ||
26 | /// <param name="client">TcpClient with the connection.</param> | ||
27 | public XmlRpcResponder(XmlRpcServer server, TcpClient client) | ||
28 | { | ||
29 | _server = server; | ||
30 | _client = client; | ||
31 | _httpReq = new SimpleHttpRequest(_client); | ||
32 | } | ||
33 | |||
34 | /// <summary>Call close to insure proper shutdown.</summary> | ||
35 | ~XmlRpcResponder() | ||
36 | { | ||
37 | Close(); | ||
38 | } | ||
39 | |||
40 | ///<summary>Respond using this responders HttpReq.</summary> | ||
41 | public void Respond() | ||
42 | { | ||
43 | Respond(HttpReq); | ||
44 | } | ||
45 | |||
46 | /// <summary>Handle an HTTP request containing an XML-RPC request.</summary> | ||
47 | /// <remarks>This method deserializes the XML-RPC request, invokes the | ||
48 | /// described method, serializes the response (or fault) and sends the XML-RPC response | ||
49 | /// back as a valid HTTP page. | ||
50 | /// </remarks> | ||
51 | /// <param name="httpReq"><c>SimpleHttpRequest</c> containing the request.</param> | ||
52 | public void Respond(SimpleHttpRequest httpReq) | ||
53 | { | ||
54 | XmlRpcRequest xmlRpcReq = (XmlRpcRequest)_deserializer.Deserialize(httpReq.Input); | ||
55 | XmlRpcResponse xmlRpcResp = new XmlRpcResponse(); | ||
56 | |||
57 | try | ||
58 | { | ||
59 | xmlRpcResp.Value = _server.Invoke(xmlRpcReq); | ||
60 | } | ||
61 | catch (XmlRpcException e) | ||
62 | { | ||
63 | xmlRpcResp.SetFault(e.FaultCode, e.FaultString); | ||
64 | } | ||
65 | catch (Exception e2) | ||
66 | { | ||
67 | xmlRpcResp.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, | ||
68 | XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); | ||
69 | } | ||
70 | |||
71 | if (Logger.Delegate != null) | ||
72 | Logger.WriteEntry(xmlRpcResp.ToString(), LogLevel.Information); | ||
73 | |||
74 | XmlRpcServer.HttpHeader(httpReq.Protocol, "text/xml", 0, " 200 OK", httpReq.Output); | ||
75 | httpReq.Output.Flush(); | ||
76 | XmlTextWriter xml = new XmlTextWriter(httpReq.Output); | ||
77 | _serializer.Serialize(xml, xmlRpcResp); | ||
78 | xml.Flush(); | ||
79 | httpReq.Output.Flush(); | ||
80 | } | ||
81 | |||
82 | ///<summary>Close all contained resources, both the HttpReq and client.</summary> | ||
83 | public void Close() | ||
84 | { | ||
85 | if (_httpReq != null) | ||
86 | { | ||
87 | _httpReq.Close(); | ||
88 | _httpReq = null; | ||
89 | } | ||
90 | |||
91 | if (_client != null) | ||
92 | { | ||
93 | _client.Close(); | ||
94 | _client = null; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
diff --git a/XmlRpcCS/XmlRpcResponse.cs b/XmlRpcCS/XmlRpcResponse.cs new file mode 100644 index 0000000..8ff8354 --- /dev/null +++ b/XmlRpcCS/XmlRpcResponse.cs | |||
@@ -0,0 +1,85 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | |||
8 | /// <summary>Class designed to represent an XML-RPC response.</summary> | ||
9 | public class XmlRpcResponse | ||
10 | { | ||
11 | private Object _value; | ||
12 | /// <summary><c>bool</c> indicating if this response represents a fault.</summary> | ||
13 | public bool IsFault; | ||
14 | |||
15 | /// <summary>Basic constructor</summary> | ||
16 | public XmlRpcResponse() | ||
17 | { | ||
18 | Value = null; | ||
19 | IsFault = false; | ||
20 | } | ||
21 | |||
22 | /// <summary>Constructor for a fault.</summary> | ||
23 | /// <param name="code"><c>int</c> the numeric faultCode value.</param> | ||
24 | /// <param name="message"><c>String</c> the faultString value.</param> | ||
25 | public XmlRpcResponse(int code, String message) | ||
26 | : this() | ||
27 | { | ||
28 | SetFault(code, message); | ||
29 | } | ||
30 | |||
31 | /// <summary>The data value of the response, may be fault data.</summary> | ||
32 | public Object Value | ||
33 | { | ||
34 | get { return _value; } | ||
35 | set | ||
36 | { | ||
37 | IsFault = false; | ||
38 | _value = value; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | /// <summary>The faultCode if this is a fault.</summary> | ||
43 | public int FaultCode | ||
44 | { | ||
45 | get | ||
46 | { | ||
47 | if (!IsFault) | ||
48 | return 0; | ||
49 | else | ||
50 | return (int)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_CODE]; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /// <summary>The faultString if this is a fault.</summary> | ||
55 | public String FaultString | ||
56 | { | ||
57 | get | ||
58 | { | ||
59 | if (!IsFault) | ||
60 | return ""; | ||
61 | else | ||
62 | return (String)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_STRING]; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /// <summary>Set this response to be a fault.</summary> | ||
67 | /// <param name="code"><c>int</c> the numeric faultCode value.</param> | ||
68 | /// <param name="message"><c>String</c> the faultString value.</param> | ||
69 | public void SetFault(int code, String message) | ||
70 | { | ||
71 | Hashtable fault = new Hashtable(); | ||
72 | fault.Add("faultCode", code); | ||
73 | fault.Add("faultString", message); | ||
74 | Value = fault; | ||
75 | IsFault = true; | ||
76 | } | ||
77 | |||
78 | /// <summary>Form a useful string representation of the object, in this case the XML response.</summary> | ||
79 | /// <returns><c>String</c> The XML serialized XML-RPC response.</returns> | ||
80 | override public String ToString() | ||
81 | { | ||
82 | return XmlRpcResponseSerializer.Singleton.Serialize(this); | ||
83 | } | ||
84 | } | ||
85 | } | ||
diff --git a/XmlRpcCS/XmlRpcResponseDeserializer.cs b/XmlRpcCS/XmlRpcResponseDeserializer.cs new file mode 100644 index 0000000..032d8a3 --- /dev/null +++ b/XmlRpcCS/XmlRpcResponseDeserializer.cs | |||
@@ -0,0 +1,65 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | |||
8 | /// <summary>Class to deserialize XML data representing a response.</summary> | ||
9 | public class XmlRpcResponseDeserializer : XmlRpcDeserializer | ||
10 | { | ||
11 | static private XmlRpcResponseDeserializer _singleton; | ||
12 | /// <summary>A static singleton instance of this deserializer.</summary> | ||
13 | [Obsolete("This object is now thread safe, just use an instance.", false)] | ||
14 | static public XmlRpcResponseDeserializer Singleton | ||
15 | { | ||
16 | get | ||
17 | { | ||
18 | if (_singleton == null) | ||
19 | _singleton = new XmlRpcResponseDeserializer(); | ||
20 | |||
21 | return _singleton; | ||
22 | } | ||
23 | } | ||
24 | |||
25 | /// <summary>Static method that parses XML data into a response using the Singleton.</summary> | ||
26 | /// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC response.</param> | ||
27 | /// <returns><c>XmlRpcResponse</c> object resulting from the parse.</returns> | ||
28 | override public Object Deserialize(TextReader xmlData) | ||
29 | { | ||
30 | XmlTextReader reader = new XmlTextReader(xmlData); | ||
31 | XmlRpcResponse response = new XmlRpcResponse(); | ||
32 | bool done = false; | ||
33 | |||
34 | lock (this) | ||
35 | { | ||
36 | Reset(); | ||
37 | |||
38 | while (!done && reader.Read()) | ||
39 | { | ||
40 | DeserializeNode(reader); // Parent parse... | ||
41 | switch (reader.NodeType) | ||
42 | { | ||
43 | case XmlNodeType.EndElement: | ||
44 | switch (reader.Name) | ||
45 | { | ||
46 | case FAULT: | ||
47 | response.Value = _value; | ||
48 | response.IsFault = true; | ||
49 | break; | ||
50 | case PARAM: | ||
51 | response.Value = _value; | ||
52 | _value = null; | ||
53 | _text = null; | ||
54 | break; | ||
55 | } | ||
56 | break; | ||
57 | default: | ||
58 | break; | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | return response; | ||
63 | } | ||
64 | } | ||
65 | } | ||
diff --git a/XmlRpcCS/XmlRpcResponseSerializer.cs b/XmlRpcCS/XmlRpcResponseSerializer.cs new file mode 100644 index 0000000..72ca568 --- /dev/null +++ b/XmlRpcCS/XmlRpcResponseSerializer.cs | |||
@@ -0,0 +1,57 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.Xml; | ||
6 | |||
7 | /// <summary>Class responsible for serializing an XML-RPC response.</summary> | ||
8 | /// <remarks>This class handles the response envelope, depending on XmlRpcSerializer | ||
9 | /// to serialize the payload.</remarks> | ||
10 | /// <seealso cref="XmlRpcSerializer"/> | ||
11 | public class XmlRpcResponseSerializer : XmlRpcSerializer | ||
12 | { | ||
13 | static private XmlRpcResponseSerializer _singleton; | ||
14 | /// <summary>A static singleton instance of this deserializer.</summary> | ||
15 | static public XmlRpcResponseSerializer Singleton | ||
16 | { | ||
17 | get | ||
18 | { | ||
19 | if (_singleton == null) | ||
20 | _singleton = new XmlRpcResponseSerializer(); | ||
21 | |||
22 | return _singleton; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | /// <summary>Serialize the <c>XmlRpcResponse</c> to the output stream.</summary> | ||
27 | /// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param> | ||
28 | /// <param name="obj">An <c>Object</c> to serialize.</param> | ||
29 | /// <seealso cref="XmlRpcResponse"/> | ||
30 | override public void Serialize(XmlTextWriter output, Object obj) | ||
31 | { | ||
32 | XmlRpcResponse response = (XmlRpcResponse)obj; | ||
33 | |||
34 | output.WriteStartDocument(); | ||
35 | output.WriteStartElement(METHOD_RESPONSE); | ||
36 | |||
37 | if (response.IsFault) | ||
38 | output.WriteStartElement(FAULT); | ||
39 | else | ||
40 | { | ||
41 | output.WriteStartElement(PARAMS); | ||
42 | output.WriteStartElement(PARAM); | ||
43 | } | ||
44 | |||
45 | output.WriteStartElement(VALUE); | ||
46 | |||
47 | SerializeObject(output, response.Value); | ||
48 | |||
49 | output.WriteEndElement(); | ||
50 | |||
51 | output.WriteEndElement(); | ||
52 | if (!response.IsFault) | ||
53 | output.WriteEndElement(); | ||
54 | output.WriteEndElement(); | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/XmlRpcCS/XmlRpcSerializer.cs b/XmlRpcCS/XmlRpcSerializer.cs new file mode 100644 index 0000000..0643d38 --- /dev/null +++ b/XmlRpcCS/XmlRpcSerializer.cs | |||
@@ -0,0 +1,109 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Xml; | ||
7 | |||
8 | /// <summary>Base class of classes serializing data to XML-RPC's XML format.</summary> | ||
9 | /// <remarks>This class handles the basic type conversions like Integer to <i4>. </remarks> | ||
10 | /// <seealso cref="XmlRpcXmlTokens"/> | ||
11 | public class XmlRpcSerializer : XmlRpcXmlTokens | ||
12 | { | ||
13 | |||
14 | /// <summary>Serialize the <c>XmlRpcRequest</c> to the output stream.</summary> | ||
15 | /// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param> | ||
16 | /// <param name="obj">An <c>Object</c> to serialize.</param> | ||
17 | /// <seealso cref="XmlRpcRequest"/> | ||
18 | virtual public void Serialize(XmlTextWriter output, Object obj) | ||
19 | { | ||
20 | } | ||
21 | |||
22 | /// <summary>Serialize the <c>XmlRpcRequest</c> to a String.</summary> | ||
23 | /// <remarks>Note this may represent a real memory hog for a large request.</remarks> | ||
24 | /// <param name="obj">An <c>Object</c> to serialize.</param> | ||
25 | /// <returns><c>String</c> containing XML-RPC representation of the request.</returns> | ||
26 | /// <seealso cref="XmlRpcRequest"/> | ||
27 | public String Serialize(Object obj) | ||
28 | { | ||
29 | StringWriter strBuf = new StringWriter(); | ||
30 | XmlTextWriter xml = new XmlTextWriter(strBuf); | ||
31 | xml.Formatting = Formatting.Indented; | ||
32 | xml.Indentation = 4; | ||
33 | Serialize(xml, obj); | ||
34 | xml.Flush(); | ||
35 | String returns = strBuf.ToString(); | ||
36 | xml.Close(); | ||
37 | return returns; | ||
38 | } | ||
39 | |||
40 | /// <remarks>Serialize the object to the output stream.</remarks> | ||
41 | /// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param> | ||
42 | /// <param name="obj">An <c>Object</c> to serialize.</param> | ||
43 | public void SerializeObject(XmlTextWriter output, Object obj) | ||
44 | { | ||
45 | if (obj == null) | ||
46 | return; | ||
47 | |||
48 | if (obj is byte[]) | ||
49 | { | ||
50 | byte[] ba = (byte[])obj; | ||
51 | output.WriteStartElement(BASE64); | ||
52 | output.WriteBase64(ba, 0, ba.Length); | ||
53 | output.WriteEndElement(); | ||
54 | } | ||
55 | else if (obj is String) | ||
56 | { | ||
57 | output.WriteElementString(STRING, obj.ToString()); | ||
58 | } | ||
59 | else if (obj is Int32) | ||
60 | { | ||
61 | output.WriteElementString(INT, obj.ToString()); | ||
62 | } | ||
63 | else if (obj is DateTime) | ||
64 | { | ||
65 | output.WriteElementString(DATETIME, ((DateTime)obj).ToString(ISO_DATETIME)); | ||
66 | } | ||
67 | else if (obj is Double) | ||
68 | { | ||
69 | output.WriteElementString(DOUBLE, obj.ToString()); | ||
70 | } | ||
71 | else if (obj is Boolean) | ||
72 | { | ||
73 | output.WriteElementString(BOOLEAN, ((((Boolean)obj) == true) ? "1" : "0")); | ||
74 | } | ||
75 | else if (obj is IList) | ||
76 | { | ||
77 | output.WriteStartElement(ARRAY); | ||
78 | output.WriteStartElement(DATA); | ||
79 | if (((ArrayList)obj).Count > 0) | ||
80 | { | ||
81 | foreach (Object member in ((IList)obj)) | ||
82 | { | ||
83 | output.WriteStartElement(VALUE); | ||
84 | SerializeObject(output, member); | ||
85 | output.WriteEndElement(); | ||
86 | } | ||
87 | } | ||
88 | output.WriteEndElement(); | ||
89 | output.WriteEndElement(); | ||
90 | } | ||
91 | else if (obj is IDictionary) | ||
92 | { | ||
93 | IDictionary h = (IDictionary)obj; | ||
94 | output.WriteStartElement(STRUCT); | ||
95 | foreach (String key in h.Keys) | ||
96 | { | ||
97 | output.WriteStartElement(MEMBER); | ||
98 | output.WriteElementString(NAME, key); | ||
99 | output.WriteStartElement(VALUE); | ||
100 | SerializeObject(output, h[key]); | ||
101 | output.WriteEndElement(); | ||
102 | output.WriteEndElement(); | ||
103 | } | ||
104 | output.WriteEndElement(); | ||
105 | } | ||
106 | |||
107 | } | ||
108 | } | ||
109 | } | ||
diff --git a/XmlRpcCS/XmlRpcServer.cs b/XmlRpcCS/XmlRpcServer.cs new file mode 100644 index 0000000..1c226c1 --- /dev/null +++ b/XmlRpcCS/XmlRpcServer.cs | |||
@@ -0,0 +1,239 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.IO; | ||
6 | using System.Net; | ||
7 | using System.Net.Sockets; | ||
8 | using System.Text; | ||
9 | using System.Threading; | ||
10 | using System.Xml; | ||
11 | |||
12 | /// <summary>A restricted HTTP server for use with XML-RPC.</summary> | ||
13 | /// <remarks>It only handles POST requests, and only POSTs representing XML-RPC calls. | ||
14 | /// In addition to dispatching requests it also provides a registry for request handlers. | ||
15 | /// </remarks> | ||
16 | public class XmlRpcServer : IEnumerable | ||
17 | { | ||
18 | #pragma warning disable 0414 // disable "private field assigned but not used" | ||
19 | const int RESPONDER_COUNT = 10; | ||
20 | private TcpListener _myListener; | ||
21 | private int _port; | ||
22 | private IPAddress _address; | ||
23 | private IDictionary _handlers; | ||
24 | private XmlRpcSystemObject _system; | ||
25 | private WaitCallback _wc; | ||
26 | #pragma warning restore 0414 | ||
27 | |||
28 | ///<summary>Constructor with port and address.</summary> | ||
29 | ///<remarks>This constructor sets up a TcpListener listening on the | ||
30 | ///given port and address. It also calls a Thread on the method StartListen().</remarks> | ||
31 | ///<param name="address"><c>IPAddress</c> value of the address to listen on.</param> | ||
32 | ///<param name="port"><c>Int</c> value of the port to listen on.</param> | ||
33 | public XmlRpcServer(IPAddress address, int port) | ||
34 | { | ||
35 | _port = port; | ||
36 | _address = address; | ||
37 | _handlers = new Hashtable(); | ||
38 | _system = new XmlRpcSystemObject(this); | ||
39 | _wc = new WaitCallback(WaitCallback); | ||
40 | } | ||
41 | |||
42 | ///<summary>Basic constructor.</summary> | ||
43 | ///<remarks>This constructor sets up a TcpListener listening on the | ||
44 | ///given port. It also calls a Thread on the method StartListen(). IPAddress.Any | ||
45 | ///is assumed as the address here.</remarks> | ||
46 | ///<param name="port"><c>Int</c> value of the port to listen on.</param> | ||
47 | public XmlRpcServer(int port) : this(IPAddress.Any, port) { } | ||
48 | |||
49 | /// <summary>Start the server.</summary> | ||
50 | public void Start() | ||
51 | { | ||
52 | try | ||
53 | { | ||
54 | Stop(); | ||
55 | //start listing on the given port | ||
56 | // IPAddress addr = IPAddress.Parse("127.0.0.1"); | ||
57 | lock (this) | ||
58 | { | ||
59 | _myListener = new TcpListener(IPAddress.Any, _port); | ||
60 | _myListener.Start(); | ||
61 | //start the thread which calls the method 'StartListen' | ||
62 | Thread th = new Thread(new ThreadStart(StartListen)); | ||
63 | th.Start(); | ||
64 | } | ||
65 | } | ||
66 | catch (Exception e) | ||
67 | { | ||
68 | Logger.WriteEntry("An Exception Occurred while Listening :" + e.ToString(), LogLevel.Error); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /// <summary>Stop the server.</summary> | ||
73 | public void Stop() | ||
74 | { | ||
75 | try | ||
76 | { | ||
77 | if (_myListener != null) | ||
78 | { | ||
79 | lock (this) | ||
80 | { | ||
81 | _myListener.Stop(); | ||
82 | _myListener = null; | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | catch (Exception e) | ||
87 | { | ||
88 | Logger.WriteEntry("An Exception Occurred while stopping :" + | ||
89 | e.ToString(), LogLevel.Error); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /// <summary>Get an enumeration of my XML-RPC handlers.</summary> | ||
94 | /// <returns><c>IEnumerable</c> the handler enumeration.</returns> | ||
95 | public IEnumerator GetEnumerator() | ||
96 | { | ||
97 | return _handlers.GetEnumerator(); | ||
98 | } | ||
99 | |||
100 | /// <summary>Retrieve a handler by name.</summary> | ||
101 | /// <param name="name"><c>String</c> naming a handler</param> | ||
102 | /// <returns><c>Object</c> that is the handler.</returns> | ||
103 | public Object this[String name] | ||
104 | { | ||
105 | get { return _handlers[name]; } | ||
106 | } | ||
107 | |||
108 | ///<summary> | ||
109 | ///This method Accepts new connections and dispatches them when appropriate. | ||
110 | ///</summary> | ||
111 | public void StartListen() | ||
112 | { | ||
113 | while (true && _myListener != null) | ||
114 | { | ||
115 | //Accept a new connection | ||
116 | XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient()); | ||
117 | ThreadPool.QueueUserWorkItem(_wc, responder); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | ///<summary> | ||
123 | ///Add an XML-RPC handler object by name. | ||
124 | ///</summary> | ||
125 | ///<param name="name"><c>String</c> XML-RPC dispatch name of this object.</param> | ||
126 | ///<param name="obj"><c>Object</c> The object that is the XML-RPC handler.</param> | ||
127 | public void Add(String name, Object obj) | ||
128 | { | ||
129 | _handlers.Add(name, obj); | ||
130 | } | ||
131 | |||
132 | ///<summary>Return a C# object.method name for and XML-RPC object.method name pair.</summary> | ||
133 | ///<param name="methodName">The XML-RPC object.method.</param> | ||
134 | ///<returns><c>String</c> of form object.method for the underlying C# method.</returns> | ||
135 | public String MethodName(String methodName) | ||
136 | { | ||
137 | int dotAt = methodName.LastIndexOf('.'); | ||
138 | |||
139 | if (dotAt == -1) | ||
140 | { | ||
141 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
142 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName); | ||
143 | } | ||
144 | |||
145 | String objectName = methodName.Substring(0, dotAt); | ||
146 | Object target = _handlers[objectName]; | ||
147 | |||
148 | if (target == null) | ||
149 | { | ||
150 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
151 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); | ||
152 | } | ||
153 | |||
154 | return target.GetType().FullName + "." + methodName.Substring(dotAt + 1); | ||
155 | } | ||
156 | |||
157 | ///<summary>Invoke a method described in a request.</summary> | ||
158 | ///<param name="req"><c>XmlRpcRequest</c> containing a method descriptions.</param> | ||
159 | /// <seealso cref="XmlRpcSystemObject.Invoke"/> | ||
160 | /// <seealso cref="XmlRpcServer.Invoke(String,String,IList)"/> | ||
161 | public Object Invoke(XmlRpcRequest req) | ||
162 | { | ||
163 | return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params); | ||
164 | } | ||
165 | |||
166 | ///<summary>Invoke a method on a named handler.</summary> | ||
167 | ///<param name="objectName"><c>String</c> The name of the handler.</param> | ||
168 | ///<param name="methodName"><c>String</c> The name of the method to invoke on the handler.</param> | ||
169 | ///<param name="parameters"><c>IList</c> The parameters to invoke the method with.</param> | ||
170 | /// <seealso cref="XmlRpcSystemObject.Invoke"/> | ||
171 | public Object Invoke(String objectName, String methodName, IList parameters) | ||
172 | { | ||
173 | Object target = _handlers[objectName]; | ||
174 | |||
175 | if (target == null) | ||
176 | { | ||
177 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
178 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); | ||
179 | } | ||
180 | |||
181 | return XmlRpcSystemObject.Invoke(target, methodName, parameters); | ||
182 | } | ||
183 | |||
184 | /// <summary>The method the thread pool invokes when a thread is available to handle an HTTP request.</summary> | ||
185 | /// <param name="responder">TcpClient from the socket accept.</param> | ||
186 | public void WaitCallback(object responder) | ||
187 | { | ||
188 | XmlRpcResponder resp = (XmlRpcResponder)responder; | ||
189 | |||
190 | if (resp.HttpReq.HttpMethod == "POST") | ||
191 | { | ||
192 | try | ||
193 | { | ||
194 | resp.Respond(); | ||
195 | } | ||
196 | catch (Exception e) | ||
197 | { | ||
198 | Logger.WriteEntry("Failed on post: " + e, LogLevel.Error); | ||
199 | } | ||
200 | } | ||
201 | else | ||
202 | { | ||
203 | Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod + | ||
204 | " ignored", LogLevel.Error); | ||
205 | } | ||
206 | |||
207 | resp.Close(); | ||
208 | } | ||
209 | |||
210 | /// <summary> | ||
211 | /// This function send the Header Information to the client (Browser) | ||
212 | /// </summary> | ||
213 | /// <param name="sHttpVersion">HTTP Version</param> | ||
214 | /// <param name="sMIMEHeader">Mime Type</param> | ||
215 | /// <param name="iTotBytes">Total Bytes to be sent in the body</param> | ||
216 | /// <param name="sStatusCode"></param> | ||
217 | /// <param name="output">Socket reference</param> | ||
218 | static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output) | ||
219 | { | ||
220 | String sBuffer = ""; | ||
221 | |||
222 | // if Mime type is not provided set default to text/html | ||
223 | if (sMIMEHeader.Length == 0) | ||
224 | { | ||
225 | sMIMEHeader = "text/html"; // Default Mime Type is text/html | ||
226 | } | ||
227 | |||
228 | sBuffer += sHttpVersion + sStatusCode + "\r\n"; | ||
229 | sBuffer += "Connection: close\r\n"; | ||
230 | if (iTotBytes > 0) | ||
231 | sBuffer += "Content-Length: " + iTotBytes + "\r\n"; | ||
232 | sBuffer += "Server: XmlRpcServer \r\n"; | ||
233 | sBuffer += "Content-Type: " + sMIMEHeader + "\r\n"; | ||
234 | sBuffer += "\r\n"; | ||
235 | |||
236 | output.Write(sBuffer); | ||
237 | } | ||
238 | } | ||
239 | } | ||
diff --git a/XmlRpcCS/XmlRpcSystemObject.cs b/XmlRpcCS/XmlRpcSystemObject.cs new file mode 100644 index 0000000..5f79951 --- /dev/null +++ b/XmlRpcCS/XmlRpcSystemObject.cs | |||
@@ -0,0 +1,252 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | using System.Collections; | ||
5 | using System.Reflection; | ||
6 | |||
7 | /// <summary> XML-RPC System object implementation of extended specifications.</summary> | ||
8 | [XmlRpcExposed] | ||
9 | public class XmlRpcSystemObject | ||
10 | { | ||
11 | private XmlRpcServer _server; | ||
12 | static private IDictionary _methodHelp = new Hashtable(); | ||
13 | |||
14 | /// <summary>Static <c>IDictionary</c> to hold mappings of method name to associated documentation String</summary> | ||
15 | static public IDictionary MethodHelp | ||
16 | { | ||
17 | get { return _methodHelp; } | ||
18 | } | ||
19 | |||
20 | /// <summary>Constructor.</summary> | ||
21 | /// <param name="server"><c>XmlRpcServer</c> server to be the system object for.</param> | ||
22 | public XmlRpcSystemObject(XmlRpcServer server) | ||
23 | { | ||
24 | _server = server; | ||
25 | server.Add("system", this); | ||
26 | _methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description."); | ||
27 | } | ||
28 | |||
29 | /// <summary>Invoke a method on a given object.</summary> | ||
30 | /// <remarks>Using reflection, and respecting the <c>XmlRpcExposed</c> attribute, | ||
31 | /// invoke the <paramref>methodName</paramref> method on the <paramref>target</paramref> | ||
32 | /// instance with the <paramref>parameters</paramref> provided. All this packages other <c>Invoke</c> methods | ||
33 | /// end up calling this.</remarks> | ||
34 | /// <returns><c>Object</c> the value the invoked method returns.</returns> | ||
35 | /// <exception cref="XmlRpcException">If method does not exist, is not exposed, parameters invalid, or invocation | ||
36 | /// results in an exception. Note, the <c>XmlRpcException.Code</c> will indicate cause.</exception> | ||
37 | static public Object Invoke(Object target, String methodName, IList parameters) | ||
38 | { | ||
39 | if (target == null) | ||
40 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
41 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object."); | ||
42 | |||
43 | Type type = target.GetType(); | ||
44 | MethodInfo method = type.GetMethod(methodName); | ||
45 | |||
46 | try | ||
47 | { | ||
48 | if (!XmlRpcExposedAttribute.ExposedMethod(target, methodName)) | ||
49 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
50 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed."); | ||
51 | } | ||
52 | catch (MissingMethodException me) | ||
53 | { | ||
54 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, | ||
55 | XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message); | ||
56 | } | ||
57 | |||
58 | Object[] args = new Object[parameters.Count]; | ||
59 | |||
60 | int index = 0; | ||
61 | foreach (Object arg in parameters) | ||
62 | { | ||
63 | args[index] = arg; | ||
64 | index++; | ||
65 | } | ||
66 | |||
67 | try | ||
68 | { | ||
69 | Object retValue = method.Invoke(target, args); | ||
70 | if (retValue == null) | ||
71 | throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, | ||
72 | XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL."); | ||
73 | return retValue; | ||
74 | } | ||
75 | catch (XmlRpcException e) | ||
76 | { | ||
77 | throw e; | ||
78 | } | ||
79 | catch (ArgumentException ae) | ||
80 | { | ||
81 | Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message, | ||
82 | LogLevel.Information); | ||
83 | String call = methodName + "( "; | ||
84 | foreach (Object o in args) | ||
85 | { | ||
86 | call += o.GetType().Name; | ||
87 | call += " "; | ||
88 | } | ||
89 | call += ")"; | ||
90 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, | ||
91 | XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call); | ||
92 | } | ||
93 | catch (TargetParameterCountException tpce) | ||
94 | { | ||
95 | Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message, | ||
96 | LogLevel.Information); | ||
97 | throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, | ||
98 | XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName); | ||
99 | } | ||
100 | catch (TargetInvocationException tie) | ||
101 | { | ||
102 | throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, | ||
103 | XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | /// <summary>List methods available on all handlers of this server.</summary> | ||
108 | /// <returns><c>IList</c> An array of <c>Strings</c>, each <c>String</c> will have form "object.method".</returns> | ||
109 | [XmlRpcExposed] | ||
110 | public IList listMethods() | ||
111 | { | ||
112 | IList methods = new ArrayList(); | ||
113 | Boolean considerExposure; | ||
114 | |||
115 | foreach (DictionaryEntry handlerEntry in _server) | ||
116 | { | ||
117 | considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType()); | ||
118 | |||
119 | foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers()) | ||
120 | { | ||
121 | if (mi.MemberType != MemberTypes.Method) | ||
122 | continue; | ||
123 | |||
124 | if (!((MethodInfo)mi).IsPublic) | ||
125 | continue; | ||
126 | |||
127 | if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi)) | ||
128 | continue; | ||
129 | |||
130 | methods.Add(handlerEntry.Key + "." + mi.Name); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return methods; | ||
135 | } | ||
136 | |||
137 | /// <summary>Given a method name return the possible signatures for it.</summary> | ||
138 | /// <param name="name"><c>String</c> The object.method name to look up.</param> | ||
139 | /// <returns><c>IList</c> Of arrays of signatures.</returns> | ||
140 | [XmlRpcExposed] | ||
141 | public IList methodSignature(String name) | ||
142 | { | ||
143 | IList signatures = new ArrayList(); | ||
144 | int index = name.IndexOf('.'); | ||
145 | |||
146 | if (index < 0) | ||
147 | return signatures; | ||
148 | |||
149 | String oName = name.Substring(0, index); | ||
150 | Object obj = _server[oName]; | ||
151 | |||
152 | if (obj == null) | ||
153 | return signatures; | ||
154 | |||
155 | MemberInfo[] mi = obj.GetType().GetMember(name.Substring(index + 1)); | ||
156 | |||
157 | if (mi == null || mi.Length != 1) // for now we want a single signature | ||
158 | return signatures; | ||
159 | |||
160 | MethodInfo method; | ||
161 | |||
162 | try | ||
163 | { | ||
164 | method = (MethodInfo)mi[0]; | ||
165 | } | ||
166 | catch (Exception e) | ||
167 | { | ||
168 | Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e, | ||
169 | LogLevel.Information); | ||
170 | return signatures; | ||
171 | } | ||
172 | |||
173 | if (!method.IsPublic) | ||
174 | return signatures; | ||
175 | |||
176 | IList signature = new ArrayList(); | ||
177 | signature.Add(method.ReturnType.Name); | ||
178 | |||
179 | foreach (ParameterInfo param in method.GetParameters()) | ||
180 | { | ||
181 | signature.Add(param.ParameterType.Name); | ||
182 | } | ||
183 | |||
184 | |||
185 | signatures.Add(signature); | ||
186 | |||
187 | return signatures; | ||
188 | } | ||
189 | |||
190 | /// <summary>Help for given method signature. Not implemented yet.</summary> | ||
191 | /// <param name="name"><c>String</c> The object.method name to look up.</param> | ||
192 | /// <returns><c>String</c> help text. Rich HTML text.</returns> | ||
193 | [XmlRpcExposed] | ||
194 | public String methodHelp(String name) | ||
195 | { | ||
196 | String help = null; | ||
197 | |||
198 | try | ||
199 | { | ||
200 | help = (String)_methodHelp[_server.MethodName(name)]; | ||
201 | } | ||
202 | catch (XmlRpcException e) | ||
203 | { | ||
204 | throw e; | ||
205 | } | ||
206 | catch (Exception) { /* ignored */ }; | ||
207 | |||
208 | if (help == null) | ||
209 | help = "No help available for: " + name; | ||
210 | |||
211 | return help; | ||
212 | } | ||
213 | |||
214 | /// <summary>Boxcarring support method.</summary> | ||
215 | /// <param name="calls"><c>IList</c> of calls</param> | ||
216 | /// <returns><c>ArrayList</c> of results/faults.</returns> | ||
217 | [XmlRpcExposed] | ||
218 | public IList multiCall(IList calls) | ||
219 | { | ||
220 | IList responses = new ArrayList(); | ||
221 | XmlRpcResponse fault = new XmlRpcResponse(); | ||
222 | |||
223 | foreach (IDictionary call in calls) | ||
224 | { | ||
225 | try | ||
226 | { | ||
227 | XmlRpcRequest req = new XmlRpcRequest((String)call[XmlRpcXmlTokens.METHOD_NAME], | ||
228 | (ArrayList)call[XmlRpcXmlTokens.PARAMS]); | ||
229 | Object results = _server.Invoke(req); | ||
230 | IList response = new ArrayList(); | ||
231 | response.Add(results); | ||
232 | responses.Add(response); | ||
233 | } | ||
234 | catch (XmlRpcException e) | ||
235 | { | ||
236 | fault.SetFault(e.FaultCode, e.FaultString); | ||
237 | responses.Add(fault.Value); | ||
238 | } | ||
239 | catch (Exception e2) | ||
240 | { | ||
241 | fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, | ||
242 | XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); | ||
243 | responses.Add(fault.Value); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | return responses; | ||
248 | } | ||
249 | |||
250 | } | ||
251 | } | ||
252 | |||
diff --git a/XmlRpcCS/XmlRpcXmlTokens.cs b/XmlRpcCS/XmlRpcXmlTokens.cs new file mode 100644 index 0000000..50788bd --- /dev/null +++ b/XmlRpcCS/XmlRpcXmlTokens.cs | |||
@@ -0,0 +1,76 @@ | |||
1 | namespace Nwc.XmlRpc | ||
2 | { | ||
3 | using System; | ||
4 | |||
5 | /// <summary>Class collecting <c>String</c> tokens that are part of XML-RPC files.</summary> | ||
6 | public class XmlRpcXmlTokens | ||
7 | { | ||
8 | /// <summary>C# formatting string to describe an ISO 8601 date.</summary> | ||
9 | public const String ISO_DATETIME = "yyyyMMdd\\THH\\:mm\\:ss"; | ||
10 | /// <summary>Base64 field indicator.</summary> | ||
11 | /// <remarks>Corresponds to the <base64> tag.</remarks> | ||
12 | public const String BASE64 = "base64"; | ||
13 | /// <summary>String field indicator.</summary> | ||
14 | /// <remarks>Corresponds to the <string> tag.</remarks> | ||
15 | public const String STRING = "string"; | ||
16 | /// <summary>Integer field integer.</summary> | ||
17 | /// <remarks>Corresponds to the <i4> tag.</remarks> | ||
18 | public const String INT = "i4"; | ||
19 | /// <summary>Alternate integer field indicator.</summary> | ||
20 | /// <remarks>Corresponds to the <int> tag.</remarks> | ||
21 | public const String ALT_INT = "int"; | ||
22 | /// <summary>Date field indicator.</summary> | ||
23 | /// <remarks>Corresponds to the <dateTime.iso8601> tag.</remarks> | ||
24 | public const String DATETIME = "dateTime.iso8601"; | ||
25 | /// <summary>Boolean field indicator.</summary> | ||
26 | /// <remarks>Corresponds to the <boolean> tag.</remarks> | ||
27 | public const String BOOLEAN = "boolean"; | ||
28 | /// <summary>Value token.</summary> | ||
29 | /// <remarks>Corresponds to the <value> tag.</remarks> | ||
30 | public const String VALUE = "value"; | ||
31 | /// <summary>Name token.</summary> | ||
32 | /// <remarks>Corresponds to the <name> tag.</remarks> | ||
33 | public const String NAME = "name"; | ||
34 | /// <summary>Array field indicator..</summary> | ||
35 | /// <remarks>Corresponds to the <array> tag.</remarks> | ||
36 | public const String ARRAY = "array"; | ||
37 | /// <summary>Data token.</summary> | ||
38 | /// <remarks>Corresponds to the <data> tag.</remarks> | ||
39 | public const String DATA = "data"; | ||
40 | /// <summary>Member token.</summary> | ||
41 | /// <remarks>Corresponds to the <member> tag.</remarks> | ||
42 | public const String MEMBER = "member"; | ||
43 | /// <summary>Stuct field indicator.</summary> | ||
44 | /// <remarks>Corresponds to the <struct> tag.</remarks> | ||
45 | public const String STRUCT = "struct"; | ||
46 | /// <summary>Double field indicator.</summary> | ||
47 | /// <remarks>Corresponds to the <double> tag.</remarks> | ||
48 | public const String DOUBLE = "double"; | ||
49 | /// <summary>Param token.</summary> | ||
50 | /// <remarks>Corresponds to the <param> tag.</remarks> | ||
51 | public const String PARAM = "param"; | ||
52 | /// <summary>Params token.</summary> | ||
53 | /// <remarks>Corresponds to the <params> tag.</remarks> | ||
54 | public const String PARAMS = "params"; | ||
55 | /// <summary>MethodCall token.</summary> | ||
56 | /// <remarks>Corresponds to the <methodCall> tag.</remarks> | ||
57 | public const String METHOD_CALL = "methodCall"; | ||
58 | /// <summary>MethodName token.</summary> | ||
59 | /// <remarks>Corresponds to the <methodName> tag.</remarks> | ||
60 | public const String METHOD_NAME = "methodName"; | ||
61 | /// <summary>MethodResponse token</summary> | ||
62 | /// <remarks>Corresponds to the <methodResponse> tag.</remarks> | ||
63 | public const String METHOD_RESPONSE = "methodResponse"; | ||
64 | /// <summary>Fault response token.</summary> | ||
65 | /// <remarks>Corresponds to the <fault> tag.</remarks> | ||
66 | public const String FAULT = "fault"; | ||
67 | /// <summary>FaultCode token.</summary> | ||
68 | /// <remarks>Corresponds to the <faultCode> tag.</remarks> | ||
69 | public const String FAULT_CODE = "faultCode"; | ||
70 | /// <summary>FaultString token.</summary> | ||
71 | /// <remarks>Corresponds to the <faultString> tag.</remarks> | ||
72 | public const String FAULT_STRING = "faultString"; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | |||