about summary refs log tree commit diff stats
path: root/update/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'update/plugin')
-rw-r--r--update/plugin/subversion/trunk/build.xml69
-rw-r--r--update/plugin/subversion/trunk/lib/commons-logging-1.1.jarbin52915 -> 0 bytes
-rw-r--r--update/plugin/subversion/trunk/lib/ws-commons-util-1.0.2.jarbin34407 -> 0 bytes
-rw-r--r--update/plugin/subversion/trunk/lib/xmlrpc-client-3.1.jarbin45124 -> 0 bytes
-rw-r--r--update/plugin/subversion/trunk/lib/xmlrpc-common-3.1.jarbin104038 -> 0 bytes
-rw-r--r--update/plugin/subversion/trunk/lib/xmlrpc-server-3.1.jarbin75310 -> 0 bytes
-rw-r--r--update/plugin/subversion/trunk/manifest.mf2
-rw-r--r--update/plugin/subversion/trunk/nbproject/build-impl.xml627
-rw-r--r--update/plugin/subversion/trunk/nbproject/genfiles.properties8
-rw-r--r--update/plugin/subversion/trunk/nbproject/project.dtd54
-rw-r--r--update/plugin/subversion/trunk/nbproject/project.properties76
-rw-r--r--update/plugin/subversion/trunk/nbproject/project.xml16
-rw-r--r--update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/MD5.java68
-rw-r--r--update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/Main.java210
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/array_key_exists.php55
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/is_a.php47
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/is_callable.php53
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/is_scalar.php38
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/var_export.php105
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/version_compare.php179
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/xmlrpc.inc3705
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/xmlrpc_wrappers.inc819
-rw-r--r--update/plugin/subversion/trunk/xmlrpc/xmlrpcs.inc1193
23 files changed, 6194 insertions, 1130 deletions
diff --git a/update/plugin/subversion/trunk/build.xml b/update/plugin/subversion/trunk/build.xml deleted file mode 100644 index b2772f4..0000000 --- a/update/plugin/subversion/trunk/build.xml +++ /dev/null
@@ -1,69 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!-- You may freely edit this file. See commented blocks below for -->
3<!-- some examples of how to customize the build. -->
4<!-- (If you delete it and reopen the project it will be recreated.) -->
5<project name="InstaDiscUpdateSVN" default="default" basedir=".">
6 <description>Builds, tests, and runs the project InstaDiscUpdateSVN.</description>
7 <import file="nbproject/build-impl.xml"/>
8 <!--
9
10 There exist several targets which are by default empty and which can be
11 used for execution of your tasks. These targets are usually executed
12 before and after some main targets. They are:
13
14 -pre-init: called before initialization of project properties
15 -post-init: called after initialization of project properties
16 -pre-compile: called before javac compilation
17 -post-compile: called after javac compilation
18 -pre-compile-single: called before javac compilation of single file
19 -post-compile-single: called after javac compilation of single file
20 -pre-compile-test: called before javac compilation of JUnit tests
21 -post-compile-test: called after javac compilation of JUnit tests
22 -pre-compile-test-single: called before javac compilation of single JUnit test
23 -post-compile-test-single: called after javac compilation of single JUunit test
24 -pre-jar: called before JAR building
25 -post-jar: called after JAR building
26 -post-clean: called after cleaning build products
27
28 (Targets beginning with '-' are not intended to be called on their own.)
29
30 Example of inserting an obfuscator after compilation could look like this:
31
32 <target name="-post-compile">
33 <obfuscate>
34 <fileset dir="${build.classes.dir}"/>
35 </obfuscate>
36 </target>
37
38 For list of available properties check the imported
39 nbproject/build-impl.xml file.
40
41
42 Another way to customize the build is by overriding existing main targets.
43 The targets of interest are:
44
45 -init-macrodef-javac: defines macro for javac compilation
46 -init-macrodef-junit: defines macro for junit execution
47 -init-macrodef-debug: defines macro for class debugging
48 -init-macrodef-java: defines macro for class execution
49 -do-jar-with-manifest: JAR building (if you are using a manifest)
50 -do-jar-without-manifest: JAR building (if you are not using a manifest)
51 run: execution of project
52 -javadoc-build: Javadoc generation
53 test-report: JUnit report generation
54
55 An example of overriding the target for project execution could look like this:
56
57 <target name="run" depends="InstaDiscUpdateSVN-impl.jar">
58 <exec dir="bin" executable="launcher.exe">
59 <arg file="${dist.jar}"/>
60 </exec>
61 </target>
62
63 Notice that the overridden target depends on the jar target and not only on
64 the compile target as the regular run target does. Again, for a list of available
65 properties which you can use, check the target you are overriding in the
66 nbproject/build-impl.xml file.
67
68 -->
69</project>
diff --git a/update/plugin/subversion/trunk/lib/commons-logging-1.1.jar b/update/plugin/subversion/trunk/lib/commons-logging-1.1.jar deleted file mode 100644 index 2ff9bbd..0000000 --- a/update/plugin/subversion/trunk/lib/commons-logging-1.1.jar +++ /dev/null
Binary files differ
diff --git a/update/plugin/subversion/trunk/lib/ws-commons-util-1.0.2.jar b/update/plugin/subversion/trunk/lib/ws-commons-util-1.0.2.jar deleted file mode 100644 index 3fc364e..0000000 --- a/update/plugin/subversion/trunk/lib/ws-commons-util-1.0.2.jar +++ /dev/null
Binary files differ
diff --git a/update/plugin/subversion/trunk/lib/xmlrpc-client-3.1.jar b/update/plugin/subversion/trunk/lib/xmlrpc-client-3.1.jar deleted file mode 100644 index a76e6ec..0000000 --- a/update/plugin/subversion/trunk/lib/xmlrpc-client-3.1.jar +++ /dev/null
Binary files differ
diff --git a/update/plugin/subversion/trunk/lib/xmlrpc-common-3.1.jar b/update/plugin/subversion/trunk/lib/xmlrpc-common-3.1.jar deleted file mode 100644 index 862098b..0000000 --- a/update/plugin/subversion/trunk/lib/xmlrpc-common-3.1.jar +++ /dev/null
Binary files differ
diff --git a/update/plugin/subversion/trunk/lib/xmlrpc-server-3.1.jar b/update/plugin/subversion/trunk/lib/xmlrpc-server-3.1.jar deleted file mode 100644 index bb64a50..0000000 --- a/update/plugin/subversion/trunk/lib/xmlrpc-server-3.1.jar +++ /dev/null
Binary files differ
diff --git a/update/plugin/subversion/trunk/manifest.mf b/update/plugin/subversion/trunk/manifest.mf deleted file mode 100644 index 0f8d5d3..0000000 --- a/update/plugin/subversion/trunk/manifest.mf +++ /dev/null
@@ -1,2 +0,0 @@
1Manifest-Version: 1.0
2X-COMMENT: Main-Class will be added automatically by build \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/nbproject/build-impl.xml b/update/plugin/subversion/trunk/nbproject/build-impl.xml deleted file mode 100644 index ac0e506..0000000 --- a/update/plugin/subversion/trunk/nbproject/build-impl.xml +++ /dev/null
@@ -1,627 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!--
3*** GENERATED FROM project.xml - DO NOT EDIT ***
4*** EDIT ../build.xml INSTEAD ***
5
6For the purpose of easier reading the script
7is divided into following sections:
8
9 - initialization
10 - compilation
11 - jar
12 - execution
13 - debugging
14 - javadoc
15 - junit compilation
16 - junit execution
17 - junit debugging
18 - applet
19 - cleanup
20
21 -->
22<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject2="http://www.netbeans.org/ns/j2se-project/2" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="InstaDiscUpdateSVN-impl">
23 <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
24 <!--
25 ======================
26 INITIALIZATION SECTION
27 ======================
28 -->
29 <target name="-pre-init">
30 <!-- Empty placeholder for easier customization. -->
31 <!-- You can override this target in the ../build.xml file. -->
32 </target>
33 <target depends="-pre-init" name="-init-private">
34 <property file="nbproject/private/config.properties"/>
35 <property file="nbproject/private/configs/${config}.properties"/>
36 <property file="nbproject/private/private.properties"/>
37 </target>
38 <target depends="-pre-init,-init-private" name="-init-user">
39 <property file="${user.properties.file}"/>
40 <!-- The two properties below are usually overridden -->
41 <!-- by the active platform. Just a fallback. -->
42 <property name="default.javac.source" value="1.4"/>
43 <property name="default.javac.target" value="1.4"/>
44 </target>
45 <target depends="-pre-init,-init-private,-init-user" name="-init-project">
46 <property file="nbproject/configs/${config}.properties"/>
47 <property file="nbproject/project.properties"/>
48 </target>
49 <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
50 <available file="${manifest.file}" property="manifest.available"/>
51 <condition property="manifest.available+main.class">
52 <and>
53 <isset property="manifest.available"/>
54 <isset property="main.class"/>
55 <not>
56 <equals arg1="${main.class}" arg2="" trim="true"/>
57 </not>
58 </and>
59 </condition>
60 <condition property="manifest.available+main.class+mkdist.available">
61 <and>
62 <istrue value="${manifest.available+main.class}"/>
63 <isset property="libs.CopyLibs.classpath"/>
64 </and>
65 </condition>
66 <condition property="have.tests">
67 <or>
68 <available file="${test.src.dir}"/>
69 </or>
70 </condition>
71 <condition property="have.sources">
72 <or>
73 <available file="${src.dir}"/>
74 </or>
75 </condition>
76 <condition property="netbeans.home+have.tests">
77 <and>
78 <isset property="netbeans.home"/>
79 <isset property="have.tests"/>
80 </and>
81 </condition>
82 <condition property="no.javadoc.preview">
83 <and>
84 <isset property="javadoc.preview"/>
85 <isfalse value="${javadoc.preview}"/>
86 </and>
87 </condition>
88 <property name="run.jvmargs" value=""/>
89 <property name="javac.compilerargs" value=""/>
90 <property name="work.dir" value="${basedir}"/>
91 <condition property="no.deps">
92 <and>
93 <istrue value="${no.dependencies}"/>
94 </and>
95 </condition>
96 <property name="javac.debug" value="true"/>
97 <property name="javadoc.preview" value="true"/>
98 <property name="application.args" value=""/>
99 <property name="source.encoding" value="${file.encoding}"/>
100 <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
101 <and>
102 <isset property="javadoc.encoding"/>
103 <not>
104 <equals arg1="${javadoc.encoding}" arg2=""/>
105 </not>
106 </and>
107 </condition>
108 <property name="javadoc.encoding.used" value="${source.encoding}"/>
109 <property name="includes" value="**"/>
110 <property name="excludes" value=""/>
111 <property name="do.depend" value="false"/>
112 <condition property="do.depend.true">
113 <istrue value="${do.depend}"/>
114 </condition>
115 <condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
116 <and>
117 <isset property="jaxws.endorsed.dir"/>
118 <available file="nbproject/jaxws-build.xml"/>
119 </and>
120 </condition>
121 </target>
122 <target name="-post-init">
123 <!-- Empty placeholder for easier customization. -->
124 <!-- You can override this target in the ../build.xml file. -->
125 </target>
126 <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
127 <fail unless="src.dir">Must set src.dir</fail>
128 <fail unless="test.src.dir">Must set test.src.dir</fail>
129 <fail unless="build.dir">Must set build.dir</fail>
130 <fail unless="dist.dir">Must set dist.dir</fail>
131 <fail unless="build.classes.dir">Must set build.classes.dir</fail>
132 <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
133 <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
134 <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
135 <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
136 <fail unless="dist.jar">Must set dist.jar</fail>
137 </target>
138 <target name="-init-macrodef-property">
139 <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
140 <attribute name="name"/>
141 <attribute name="value"/>
142 <sequential>
143 <property name="@{name}" value="${@{value}}"/>
144 </sequential>
145 </macrodef>
146 </target>
147 <target name="-init-macrodef-javac">
148 <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
149 <attribute default="${src.dir}" name="srcdir"/>
150 <attribute default="${build.classes.dir}" name="destdir"/>
151 <attribute default="${javac.classpath}" name="classpath"/>
152 <attribute default="${includes}" name="includes"/>
153 <attribute default="${excludes}" name="excludes"/>
154 <attribute default="${javac.debug}" name="debug"/>
155 <attribute default="" name="sourcepath"/>
156 <element name="customize" optional="true"/>
157 <sequential>
158 <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}">
159 <classpath>
160 <path path="@{classpath}"/>
161 </classpath>
162 <compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
163 <customize/>
164 </javac>
165 </sequential>
166 </macrodef>
167 <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
168 <attribute default="${src.dir}" name="srcdir"/>
169 <attribute default="${build.classes.dir}" name="destdir"/>
170 <attribute default="${javac.classpath}" name="classpath"/>
171 <sequential>
172 <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
173 <classpath>
174 <path path="@{classpath}"/>
175 </classpath>
176 </depend>
177 </sequential>
178 </macrodef>
179 <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
180 <attribute default="${build.classes.dir}" name="destdir"/>
181 <sequential>
182 <fail unless="javac.includes">Must set javac.includes</fail>
183 <pathconvert pathsep="," property="javac.includes.binary">
184 <path>
185 <filelist dir="@{destdir}" files="${javac.includes}"/>
186 </path>
187 <globmapper from="*.java" to="*.class"/>
188 </pathconvert>
189 <delete>
190 <files includes="${javac.includes.binary}"/>
191 </delete>
192 </sequential>
193 </macrodef>
194 </target>
195 <target name="-init-macrodef-junit">
196 <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
197 <attribute default="${includes}" name="includes"/>
198 <attribute default="${excludes}" name="excludes"/>
199 <attribute default="**" name="testincludes"/>
200 <sequential>
201 <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
202 <batchtest todir="${build.test.results.dir}">
203 <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
204 <filename name="@{testincludes}"/>
205 </fileset>
206 </batchtest>
207 <classpath>
208 <path path="${run.test.classpath}"/>
209 </classpath>
210 <syspropertyset>
211 <propertyref prefix="test-sys-prop."/>
212 <mapper from="test-sys-prop.*" to="*" type="glob"/>
213 </syspropertyset>
214 <formatter type="brief" usefile="false"/>
215 <formatter type="xml"/>
216 <jvmarg line="${run.jvmargs}"/>
217 </junit>
218 </sequential>
219 </macrodef>
220 </target>
221 <target name="-init-macrodef-nbjpda">
222 <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
223 <attribute default="${main.class}" name="name"/>
224 <attribute default="${debug.classpath}" name="classpath"/>
225 <attribute default="" name="stopclassname"/>
226 <sequential>
227 <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="dt_socket">
228 <classpath>
229 <path path="@{classpath}"/>
230 </classpath>
231 </nbjpdastart>
232 </sequential>
233 </macrodef>
234 <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
235 <attribute default="${build.classes.dir}" name="dir"/>
236 <sequential>
237 <nbjpdareload>
238 <fileset dir="@{dir}" includes="${fix.includes}*.class"/>
239 </nbjpdareload>
240 </sequential>
241 </macrodef>
242 </target>
243 <target name="-init-debug-args">
244 <property name="version-output" value="java version &quot;${ant.java.version}"/>
245 <condition property="have-jdk-older-than-1.4">
246 <or>
247 <contains string="${version-output}" substring="java version &quot;1.0"/>
248 <contains string="${version-output}" substring="java version &quot;1.1"/>
249 <contains string="${version-output}" substring="java version &quot;1.2"/>
250 <contains string="${version-output}" substring="java version &quot;1.3"/>
251 </or>
252 </condition>
253 <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
254 <istrue value="${have-jdk-older-than-1.4}"/>
255 </condition>
256 </target>
257 <target depends="-init-debug-args" name="-init-macrodef-debug">
258 <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
259 <attribute default="${main.class}" name="classname"/>
260 <attribute default="${debug.classpath}" name="classpath"/>
261 <element name="customize" optional="true"/>
262 <sequential>
263 <java classname="@{classname}" dir="${work.dir}" fork="true">
264 <jvmarg line="${debug-args-line}"/>
265 <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
266 <jvmarg line="${run.jvmargs}"/>
267 <classpath>
268 <path path="@{classpath}"/>
269 </classpath>
270 <syspropertyset>
271 <propertyref prefix="run-sys-prop."/>
272 <mapper from="run-sys-prop.*" to="*" type="glob"/>
273 </syspropertyset>
274 <customize/>
275 </java>
276 </sequential>
277 </macrodef>
278 </target>
279 <target name="-init-macrodef-java">
280 <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
281 <attribute default="${main.class}" name="classname"/>
282 <element name="customize" optional="true"/>
283 <sequential>
284 <java classname="@{classname}" dir="${work.dir}" fork="true">
285 <jvmarg line="${run.jvmargs}"/>
286 <classpath>
287 <path path="${run.classpath}"/>
288 </classpath>
289 <syspropertyset>
290 <propertyref prefix="run-sys-prop."/>
291 <mapper from="run-sys-prop.*" to="*" type="glob"/>
292 </syspropertyset>
293 <customize/>
294 </java>
295 </sequential>
296 </macrodef>
297 </target>
298 <target name="-init-presetdef-jar">
299 <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
300 <jar compress="${jar.compress}" jarfile="${dist.jar}">
301 <j2seproject1:fileset dir="${build.classes.dir}"/>
302 </jar>
303 </presetdef>
304 </target>
305 <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
306 <!--
307 ===================
308 COMPILATION SECTION
309 ===================
310 -->
311 <target depends="init" name="deps-jar" unless="no.deps"/>
312 <target depends="init,deps-jar" name="-pre-pre-compile">
313 <mkdir dir="${build.classes.dir}"/>
314 </target>
315 <target name="-pre-compile">
316 <!-- Empty placeholder for easier customization. -->
317 <!-- You can override this target in the ../build.xml file. -->
318 </target>
319 <target if="do.depend.true" name="-compile-depend">
320 <j2seproject3:depend/>
321 </target>
322 <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
323 <j2seproject3:javac/>
324 <copy todir="${build.classes.dir}">
325 <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
326 </copy>
327 </target>
328 <target name="-post-compile">
329 <!-- Empty placeholder for easier customization. -->
330 <!-- You can override this target in the ../build.xml file. -->
331 </target>
332 <target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
333 <target name="-pre-compile-single">
334 <!-- Empty placeholder for easier customization. -->
335 <!-- You can override this target in the ../build.xml file. -->
336 </target>
337 <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
338 <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
339 <j2seproject3:force-recompile/>
340 <j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
341 </target>
342 <target name="-post-compile-single">
343 <!-- Empty placeholder for easier customization. -->
344 <!-- You can override this target in the ../build.xml file. -->
345 </target>
346 <target depends="init,deps-jar,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
347 <!--
348 ====================
349 JAR BUILDING SECTION
350 ====================
351 -->
352 <target depends="init" name="-pre-pre-jar">
353 <dirname file="${dist.jar}" property="dist.jar.dir"/>
354 <mkdir dir="${dist.jar.dir}"/>
355 </target>
356 <target name="-pre-jar">
357 <!-- Empty placeholder for easier customization. -->
358 <!-- You can override this target in the ../build.xml file. -->
359 </target>
360 <target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
361 <j2seproject1:jar/>
362 </target>
363 <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
364 <j2seproject1:jar manifest="${manifest.file}"/>
365 </target>
366 <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
367 <j2seproject1:jar manifest="${manifest.file}">
368 <j2seproject1:manifest>
369 <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
370 </j2seproject1:manifest>
371 </j2seproject1:jar>
372 <echo>To run this application from the command line without Ant, try:</echo>
373 <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
374 <property location="${dist.jar}" name="dist.jar.resolved"/>
375 <pathconvert property="run.classpath.with.dist.jar">
376 <path path="${run.classpath}"/>
377 <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
378 </pathconvert>
379 <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
380 </target>
381 <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
382 <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
383 <pathconvert property="run.classpath.without.build.classes.dir">
384 <path path="${run.classpath}"/>
385 <map from="${build.classes.dir.resolved}" to=""/>
386 </pathconvert>
387 <pathconvert pathsep=" " property="jar.classpath">
388 <path path="${run.classpath.without.build.classes.dir}"/>
389 <chainedmapper>
390 <flattenmapper/>
391 <globmapper from="*" to="lib/*"/>
392 </chainedmapper>
393 </pathconvert>
394 <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
395 <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
396 <fileset dir="${build.classes.dir}"/>
397 <manifest>
398 <attribute name="Main-Class" value="${main.class}"/>
399 <attribute name="Class-Path" value="${jar.classpath}"/>
400 </manifest>
401 </copylibs>
402 <echo>To run this application from the command line without Ant, try:</echo>
403 <property location="${dist.jar}" name="dist.jar.resolved"/>
404 <echo>java -jar "${dist.jar.resolved}"</echo>
405 </target>
406 <target name="-post-jar">
407 <!-- Empty placeholder for easier customization. -->
408 <!-- You can override this target in the ../build.xml file. -->
409 </target>
410 <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
411 <!--
412 =================
413 EXECUTION SECTION
414 =================
415 -->
416 <target depends="init,compile" description="Run a main class." name="run">
417 <j2seproject1:java>
418 <customize>
419 <arg line="${application.args}"/>
420 </customize>
421 </j2seproject1:java>
422 </target>
423 <target name="-do-not-recompile">
424 <property name="javac.includes.binary" value=""/>
425 </target>
426 <target depends="init,-do-not-recompile,compile-single" name="run-single">
427 <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
428 <j2seproject1:java classname="${run.class}"/>
429 </target>
430 <!--
431 =================
432 DEBUGGING SECTION
433 =================
434 -->
435 <target depends="init" if="netbeans.home" name="-debug-start-debugger">
436 <j2seproject1:nbjpdastart name="${debug.class}"/>
437 </target>
438 <target depends="init,compile" name="-debug-start-debuggee">
439 <j2seproject3:debug>
440 <customize>
441 <arg line="${application.args}"/>
442 </customize>
443 </j2seproject3:debug>
444 </target>
445 <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
446 <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
447 <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
448 </target>
449 <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
450 <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
451 <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
452 <j2seproject3:debug classname="${debug.class}"/>
453 </target>
454 <target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
455 <target depends="init" name="-pre-debug-fix">
456 <fail unless="fix.includes">Must set fix.includes</fail>
457 <property name="javac.includes" value="${fix.includes}.java"/>
458 </target>
459 <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
460 <j2seproject1:nbjpdareload/>
461 </target>
462 <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
463 <!--
464 ===============
465 JAVADOC SECTION
466 ===============
467 -->
468 <target depends="init" name="-javadoc-build">
469 <mkdir dir="${dist.javadoc.dir}"/>
470 <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
471 <classpath>
472 <path path="${javac.classpath}"/>
473 </classpath>
474 <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
475 <filename name="**/*.java"/>
476 </fileset>
477 </javadoc>
478 </target>
479 <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
480 <nbbrowse file="${dist.javadoc.dir}/index.html"/>
481 </target>
482 <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
483 <!--
484 =========================
485 JUNIT COMPILATION SECTION
486 =========================
487 -->
488 <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
489 <mkdir dir="${build.test.classes.dir}"/>
490 </target>
491 <target name="-pre-compile-test">
492 <!-- Empty placeholder for easier customization. -->
493 <!-- You can override this target in the ../build.xml file. -->
494 </target>
495 <target if="do.depend.true" name="-compile-test-depend">
496 <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
497 </target>
498 <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
499 <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
500 <copy todir="${build.test.classes.dir}">
501 <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
502 </copy>
503 </target>
504 <target name="-post-compile-test">
505 <!-- Empty placeholder for easier customization. -->
506 <!-- You can override this target in the ../build.xml file. -->
507 </target>
508 <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
509 <target name="-pre-compile-test-single">
510 <!-- Empty placeholder for easier customization. -->
511 <!-- You can override this target in the ../build.xml file. -->
512 </target>
513 <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
514 <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
515 <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
516 <j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
517 <copy todir="${build.test.classes.dir}">
518 <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
519 </copy>
520 </target>
521 <target name="-post-compile-test-single">
522 <!-- Empty placeholder for easier customization. -->
523 <!-- You can override this target in the ../build.xml file. -->
524 </target>
525 <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
526 <!--
527 =======================
528 JUNIT EXECUTION SECTION
529 =======================
530 -->
531 <target depends="init" if="have.tests" name="-pre-test-run">
532 <mkdir dir="${build.test.results.dir}"/>
533 </target>
534 <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
535 <j2seproject3:junit testincludes="**/*Test.java"/>
536 </target>
537 <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
538 <fail if="tests.failed">Some tests failed; see details above.</fail>
539 </target>
540 <target depends="init" if="have.tests" name="test-report"/>
541 <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
542 <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
543 <target depends="init" if="have.tests" name="-pre-test-run-single">
544 <mkdir dir="${build.test.results.dir}"/>
545 </target>
546 <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
547 <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
548 <j2seproject3:junit excludes="" includes="${test.includes}"/>
549 </target>
550 <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
551 <fail if="tests.failed">Some tests failed; see details above.</fail>
552 </target>
553 <target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
554 <!--
555 =======================
556 JUNIT DEBUGGING SECTION
557 =======================
558 -->
559 <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
560 <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
561 <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
562 <delete file="${test.report.file}"/>
563 <mkdir dir="${build.test.results.dir}"/>
564 <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
565 <customize>
566 <syspropertyset>
567 <propertyref prefix="test-sys-prop."/>
568 <mapper from="test-sys-prop.*" to="*" type="glob"/>
569 </syspropertyset>
570 <arg value="${test.class}"/>
571 <arg value="showoutput=true"/>
572 <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
573 <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
574 </customize>
575 </j2seproject3:debug>
576 </target>
577 <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
578 <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
579 </target>
580 <target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
581 <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
582 <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
583 </target>
584 <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
585 <!--
586 =========================
587 APPLET EXECUTION SECTION
588 =========================
589 -->
590 <target depends="init,compile-single" name="run-applet">
591 <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
592 <j2seproject1:java classname="sun.applet.AppletViewer">
593 <customize>
594 <arg value="${applet.url}"/>
595 </customize>
596 </j2seproject1:java>
597 </target>
598 <!--
599 =========================
600 APPLET DEBUGGING SECTION
601 =========================
602 -->
603 <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
604 <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
605 <j2seproject3:debug classname="sun.applet.AppletViewer">
606 <customize>
607 <arg value="${applet.url}"/>
608 </customize>
609 </j2seproject3:debug>
610 </target>
611 <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
612 <!--
613 ===============
614 CLEANUP SECTION
615 ===============
616 -->
617 <target depends="init" name="deps-clean" unless="no.deps"/>
618 <target depends="init" name="-do-clean">
619 <delete dir="${build.dir}"/>
620 <delete dir="${dist.dir}"/>
621 </target>
622 <target name="-post-clean">
623 <!-- Empty placeholder for easier customization. -->
624 <!-- You can override this target in the ../build.xml file. -->
625 </target>
626 <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
627</project>
diff --git a/update/plugin/subversion/trunk/nbproject/genfiles.properties b/update/plugin/subversion/trunk/nbproject/genfiles.properties deleted file mode 100644 index 1012552..0000000 --- a/update/plugin/subversion/trunk/nbproject/genfiles.properties +++ /dev/null
@@ -1,8 +0,0 @@
1build.xml.data.CRC32=a07c171d
2build.xml.script.CRC32=10186d9c
3build.xml.stylesheet.CRC32=be360661
4# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
5# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
6nbproject/build-impl.xml.data.CRC32=a07c171d
7nbproject/build-impl.xml.script.CRC32=b31666c6
8nbproject/build-impl.xml.stylesheet.CRC32=f1d9da08
diff --git a/update/plugin/subversion/trunk/nbproject/project.dtd b/update/plugin/subversion/trunk/nbproject/project.dtd deleted file mode 100644 index 7b77177..0000000 --- a/update/plugin/subversion/trunk/nbproject/project.dtd +++ /dev/null
@@ -1,54 +0,0 @@
1<?xml version='1.0' encoding='UTF-8'?>
2
3<!--
4 TODO define vocabulary identification
5 PUBLIC ID: -//vendor//vocabulary//EN
6 SYSTEM ID: http://server/path/project.dtd
7
8--><!--
9 An example how to use this DTD from your XML document:
10
11 <?xml version="1.0"?>
12
13 <!DOCTYPE project SYSTEM "project.dtd">
14
15 <project>
16 ...
17 </project>
18-->
19
20<!--- Put your DTDDoc comment here. -->
21<!ELEMENT project (configuration|type)*>
22<!ATTLIST project
23 xmlns CDATA #IMPLIED
24 >
25
26<!--- Put your DTDDoc comment here. -->
27<!ELEMENT type (#PCDATA)>
28
29<!--- Put your DTDDoc comment here. -->
30<!ELEMENT configuration (data)*>
31
32<!--- Put your DTDDoc comment here. -->
33<!ELEMENT data (test-roots|source-roots|minimum-ant-version|name)*>
34<!ATTLIST data
35 xmlns CDATA #IMPLIED
36 >
37
38<!--- Put your DTDDoc comment here. -->
39<!ELEMENT name (#PCDATA)>
40
41<!--- Put your DTDDoc comment here. -->
42<!ELEMENT minimum-ant-version (#PCDATA)>
43
44<!--- Put your DTDDoc comment here. -->
45<!ELEMENT source-roots (root)*>
46
47<!--- Put your DTDDoc comment here. -->
48<!ELEMENT root EMPTY>
49<!ATTLIST root
50 id CDATA #IMPLIED
51 >
52
53<!--- Put your DTDDoc comment here. -->
54<!ELEMENT test-roots (root)*>
diff --git a/update/plugin/subversion/trunk/nbproject/project.properties b/update/plugin/subversion/trunk/nbproject/project.properties deleted file mode 100644 index 5c1f30f..0000000 --- a/update/plugin/subversion/trunk/nbproject/project.properties +++ /dev/null
@@ -1,76 +0,0 @@
1application.title=InstaDiscUpdateSVN
2application.vendor=hatkirby
3build.classes.dir=${build.dir}/classes
4build.classes.excludes=**/*.java,**/*.form
5# This directory is removed when the project is cleaned:
6build.dir=build
7build.generated.dir=${build.dir}/generated
8# Only compile against the classpath explicitly listed here:
9build.sysclasspath=ignore
10build.test.classes.dir=${build.dir}/test/classes
11build.test.results.dir=${build.dir}/test/results
12debug.classpath=\
13 ${run.classpath}
14debug.test.classpath=\
15 ${run.test.classpath}
16# This directory is removed when the project is cleaned:
17dist.dir=dist
18dist.jar=${dist.dir}/InstaDiscUpdateSVN.jar
19dist.javadoc.dir=${dist.dir}/javadoc
20excludes=
21file.reference.commons-logging-1.1.jar=lib/commons-logging-1.1.jar
22file.reference.ws-commons-util-1.0.2.jar=lib/ws-commons-util-1.0.2.jar
23file.reference.xmlrpc-client-3.1.jar=lib/xmlrpc-client-3.1.jar
24file.reference.xmlrpc-common-3.1.jar=lib/xmlrpc-common-3.1.jar
25file.reference.xmlrpc-server-3.1.jar=lib/xmlrpc-server-3.1.jar
26includes=**
27jar.compress=false
28javac.classpath=\
29 ${file.reference.commons-logging-1.1.jar}:\
30 ${file.reference.ws-commons-util-1.0.2.jar}:\
31 ${file.reference.xmlrpc-client-3.1.jar}:\
32 ${file.reference.xmlrpc-common-3.1.jar}:\
33 ${file.reference.xmlrpc-server-3.1.jar}
34# Space-separated list of extra javac options
35javac.compilerargs=
36javac.deprecation=false
37javac.source=1.5
38javac.target=1.5
39javac.test.classpath=\
40 ${javac.classpath}:\
41 ${build.classes.dir}:\
42 ${libs.junit.classpath}:\
43 ${libs.junit_4.classpath}
44javadoc.additionalparam=
45javadoc.author=false
46javadoc.encoding=${source.encoding}
47javadoc.noindex=false
48javadoc.nonavbar=false
49javadoc.notree=false
50javadoc.private=false
51javadoc.splitindex=true
52javadoc.use=true
53javadoc.version=false
54javadoc.windowtitle=
55jnlp.codebase.type=local
56jnlp.codebase.url=file:/home/hatkirby/NetBeansProjects/InstaDiscUpdateSVN/dist
57jnlp.enabled=false
58jnlp.offline-allowed=false
59jnlp.signed=false
60main.class=com.fourisland.instadisc.update.svn.Main
61manifest.file=manifest.mf
62meta.inf.dir=${src.dir}/META-INF
63platform.active=default_platform
64run.classpath=\
65 ${javac.classpath}:\
66 ${build.classes.dir}
67# Space-separated list of JVM arguments used when running the project
68# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
69# or test-sys-prop.name=value to set system properties for unit tests):
70run.jvmargs=
71run.test.classpath=\
72 ${javac.test.classpath}:\
73 ${build.test.classes.dir}
74source.encoding=UTF-8
75src.dir=src
76test.src.dir=test
diff --git a/update/plugin/subversion/trunk/nbproject/project.xml b/update/plugin/subversion/trunk/nbproject/project.xml deleted file mode 100644 index 1a38c15..0000000 --- a/update/plugin/subversion/trunk/nbproject/project.xml +++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<project xmlns="http://www.netbeans.org/ns/project/1">
3 <type>org.netbeans.modules.java.j2seproject</type>
4 <configuration>
5 <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
6 <name>InstaDiscUpdateSVN</name>
7 <minimum-ant-version>1.6.5</minimum-ant-version>
8 <source-roots>
9 <root id="src.dir"/>
10 </source-roots>
11 <test-roots>
12 <root id="test.src.dir"/>
13 </test-roots>
14 </data>
15 </configuration>
16</project>
diff --git a/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/MD5.java b/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/MD5.java deleted file mode 100644 index 0929022..0000000 --- a/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/MD5.java +++ /dev/null
@@ -1,68 +0,0 @@
1/*
2 * To change this template, choose Tools | Templates
3 * and open the template in the editor.
4 */
5package com.fourisland.instadisc.update.svn;
6
7import java.security.MessageDigest;
8import java.util.logging.Level;
9import java.util.logging.Logger;
10
11/**
12 *
13 * @author hatkirby
14 */
15public class MD5 {
16
17 String ver;
18
19 public MD5(String ver) {
20 this.ver = ver;
21 }
22
23 public MD5(char[] password) {
24 int i=0;
25 ver="";
26 for (i=0;i<password.length;i++)
27 {
28 ver += password[i];
29 password[i] = 0;
30 }
31 }
32
33 public String hash()
34 {
35 StringBuilder verify = new StringBuilder();
36 try {
37 MessageDigest md5 = MessageDigest.getInstance("MD5");
38 int i = 0;
39 byte[] create = new byte[ver.length()];
40 for (i = 0; i < ver.length(); i++) {
41 create[i] = (byte) ver.charAt(i);
42 }
43 byte buffer[] = md5.digest(create);
44 for (i = 0; i < buffer.length; i++) {
45 String hex = Integer.toHexString(buffer[i]);
46 verify.append(pad(hex.substring(max(hex.length() - 2, 0)),"0",2));
47 }
48 } catch (Exception ex) {
49 Logger.getLogger(MD5.class.getName()).log(Level.SEVERE, null, ex);
50 }
51 ver = "";
52 return verify.toString();
53 }
54
55 private int max(int x, int y)
56 {
57 return (x > y ? x : y);
58 }
59
60 private String pad(String in, String pad, int len)
61 {
62 while (in.length() < len)
63 {
64 in = pad + in;
65 }
66 return in;
67 }
68}
diff --git a/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/Main.java b/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/Main.java deleted file mode 100644 index 5aaafe3..0000000 --- a/update/plugin/subversion/trunk/src/com/fourisland/instadisc/update/svn/Main.java +++ /dev/null
@@ -1,210 +0,0 @@
1package com.fourisland.instadisc.update.svn;
2
3import java.io.IOException;
4import java.net.MalformedURLException;
5import java.net.URL;
6import java.security.InvalidAlgorithmParameterException;
7import java.security.InvalidKeyException;
8import java.security.NoSuchAlgorithmException;
9import java.util.Random;
10import java.util.logging.Level;
11import java.util.logging.Logger;
12import javax.crypto.BadPaddingException;
13import javax.crypto.Cipher;
14import javax.crypto.IllegalBlockSizeException;
15import javax.crypto.NoSuchPaddingException;
16import javax.crypto.spec.IvParameterSpec;
17import javax.crypto.spec.SecretKeySpec;
18import org.apache.xmlrpc.XmlRpcException;
19import org.apache.xmlrpc.client.XmlRpcClient;
20import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
21
22public class Main {
23
24 public static void main(String[] args) {
25 try
26 {
27 String pathScheme = getArg(1, args);
28 String author = getArg(2, args);
29 String seriesURL = getArg(3, args);
30 String subscriptionID = getArg(4, args);
31 String revision = getArg(5, args);
32
33 StringBuilder messBuilder = new StringBuilder();
34 byte rs = 0;
35
36 while (rs != -1)
37 {
38 try
39 {
40 rs = (byte) System.in.read();
41 if (rs != -1)
42 {
43 messBuilder.append(new String(new byte[]{rs}));
44 }
45 } catch (IOException ex)
46 {
47 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
48 }
49 }
50
51 String message = messBuilder.toString();
52 message = message.substring(0, message.indexOf("\n"));
53
54 String path = pathScheme.replace("__REV__", revision);
55
56 Random r = new Random();
57 int verID = r.nextInt(Integer.MAX_VALUE);
58 int encID = 0;
59
60 if (args.length > 7)
61 {
62 encID = r.nextInt(Integer.MAX_VALUE);
63 MD5 md5 = new MD5(padright(args[7], new Integer(encID).toString(), 16).substring(0, 16));
64 String key = md5.hash().substring(0, 16);
65 String iv = reverse(key);
66
67 try
68 {
69 Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
70 SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
71 IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
72 cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
73
74 message = new String(bytesToHex(cipher.doFinal(pad(message.getBytes())))).trim();
75 author = new String(bytesToHex(cipher.doFinal(pad(author.getBytes())))).trim();
76 path = new String(bytesToHex(cipher.doFinal(pad(path.getBytes())))).trim();
77 } catch (IllegalBlockSizeException ex)
78 {
79 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
80 } catch (BadPaddingException ex)
81 {
82 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
83 } catch (InvalidKeyException ex)
84 {
85 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
86 } catch (InvalidAlgorithmParameterException ex)
87 {
88 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
89 } catch (NoSuchAlgorithmException ex)
90 {
91 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
92 } catch (NoSuchPaddingException ex)
93 {
94 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
95 }
96 }
97
98 XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
99 config.setServerURL(new URL("http://central.fourisland.com/xmlrpc.php"));
100 XmlRpcClient client = new XmlRpcClient();
101 client.setConfig(config);
102 Integer resp = (Integer) client.execute("InstaDisc.sendFromUpdate", new Object[]{seriesURL,
103 subscriptionID,
104 message,
105 author,
106 path,
107 "a:0:{}",
108 encID
109 });
110
111 if (resp == 2)
112 {
113 main(args);
114 }
115 } catch (XmlRpcException ex)
116 {
117 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
118 } catch (MalformedURLException ex)
119 {
120 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
121 }
122 }
123
124 public static String getArg(int arg, String[] args) {
125 if (args.length < (arg + 1))
126 {
127 System.out.println("Program requires 7 arguments and you only provided " + arg);
128 System.exit(1);
129 }
130
131 return args[arg];
132 }
133
134 public static String reverse(String in) {
135 String out = "";
136 int i = 0;
137
138 for (i = 0; i < in.length(); i++)
139 {
140 out = in.charAt(i) + out;
141 }
142
143 return out;
144 }
145
146 public static String padright(String in, String pad, int len) {
147 while (in.length() < len)
148 {
149 in += pad;
150 }
151
152 if (in.length() > len)
153 {
154 in = in.substring(0, len);
155 }
156
157 return in;
158 }
159
160 public static String bytesToHex(byte[] buffer) {
161 if (buffer == null)
162 {
163 return null;
164 } else
165 {
166 StringBuilder result = new StringBuilder();
167 for (int i = 0; i < buffer.length; i++)
168 {
169 String hex = Integer.toHexString(Integer.decode(new Byte(buffer[i]).toString()));
170 result.append(padleft(hex.substring(max(hex.length() - 2, 0)), "0", 2));
171 }
172
173 return result.toString();
174 }
175 }
176
177 public static int max(int x, int y) {
178 return (x > y ? x : y);
179 }
180
181 public static String padleft(String in, String pad, int len) {
182 while (in.length() < len)
183 {
184 in = pad + in;
185 }
186
187 if (in.length() > len)
188 {
189 in = in.substring(0, len);
190 }
191
192 return in;
193 }
194
195 public static byte[] pad(byte[] buffer)
196 {
197 while (buffer.length % 16 != 0)
198 {
199 byte[] tmp = new byte[buffer.length+1];
200 int i=0;
201 for (i=0;i<buffer.length;i++)
202 {
203 tmp[i] = buffer[i];
204 }
205 tmp[buffer.length] = 0;
206 buffer = tmp;
207 }
208 return buffer;
209 }
210}
diff --git a/update/plugin/subversion/trunk/xmlrpc/array_key_exists.php b/update/plugin/subversion/trunk/xmlrpc/array_key_exists.php new file mode 100644 index 0000000..c5ae519 --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/array_key_exists.php
@@ -0,0 +1,55 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: array_key_exists.php,v 1.1 2005/07/11 16:34:35 ggiunta Exp $
19
20
21/**
22 * Replace array_key_exists()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.array_key_exists
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.1 $
29 * @since PHP 4.1.0
30 * @require PHP 4.0.0 (user_error)
31 */
32if (!function_exists('array_key_exists')) {
33 function array_key_exists($key, $search)
34 {
35 if (!is_scalar($key)) {
36 user_error('array_key_exists() The first argument should be either a string or an integer',
37 E_USER_WARNING);
38 return false;
39 }
40
41 if (is_object($search)) {
42 $search = get_object_vars($search);
43 }
44
45 if (!is_array($search)) {
46 user_error('array_key_exists() The second argument should be either an array or an object',
47 E_USER_WARNING);
48 return false;
49 }
50
51 return in_array($key, array_keys($search));
52 }
53}
54
55?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/is_a.php b/update/plugin/subversion/trunk/xmlrpc/is_a.php new file mode 100644 index 0000000..d98db1f --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/is_a.php
@@ -0,0 +1,47 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: is_a.php,v 1.2 2005/11/21 10:57:23 ggiunta Exp $
19
20
21/**
22 * Replace function is_a()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.is_a
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.2 $
29 * @since PHP 4.2.0
30 * @require PHP 4.0.0 (user_error) (is_subclass_of)
31 */
32if (!function_exists('is_a')) {
33 function is_a($object, $class)
34 {
35 if (!is_object($object)) {
36 return false;
37 }
38
39 if (get_class($object) == strtolower($class)) {
40 return true;
41 } else {
42 return is_subclass_of($object, $class);
43 }
44 }
45}
46
47?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/is_callable.php b/update/plugin/subversion/trunk/xmlrpc/is_callable.php new file mode 100644 index 0000000..b769c41 --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/is_callable.php
@@ -0,0 +1,53 @@
1<?php
2/**
3 * Replace function is_callable()
4 *
5 * @category PHP
6 * @package PHP_Compat
7 * @link http://php.net/function.is_callable
8 * @author Gaetano Giunta <giunta.gaetano@sea-aeroportimilano.it>
9 * @version $Id: is_callable.php,v 1.3 2006/08/21 14:03:15 ggiunta Exp $
10 * @since PHP 4.0.6
11 * @require PHP 4.0.0 (true, false, etc...)
12 * @todo add the 3rd parameter syntax...
13 */
14if (!function_exists('is_callable')) {
15 function is_callable($var, $syntax_only=false)
16 {
17 if ($syntax_only)
18 {
19 /* from The Manual:
20 * If the syntax_only argument is TRUE the function only verifies
21 * that var might be a function or method. It will only reject simple
22 * variables that are not strings, or an array that does not have a
23 * valid structure to be used as a callback. The valid ones are
24 * supposed to have only 2 entries, the first of which is an object
25 * or a string, and the second a string
26 */
27 return (is_string($var) || (is_array($var) && count($var) == 2 && is_string(end($var)) && (is_string(reset($var)) || is_object(reset($var)))));
28 }
29 else
30 {
31 if (is_string($var))
32 {
33 return function_exists($var);
34 }
35 else if (is_array($var) && count($var) == 2 && is_string($method = end($var)))
36 {
37 $obj = reset($var);
38 if (is_string($obj))
39 {
40 $methods = get_class_methods($obj);
41 return (bool)(is_array($methods) && in_array(strtolower($method), $methods));
42 }
43 else if (is_object($obj))
44 {
45 return method_exists($obj, $method);
46 }
47 }
48 return false;
49 }
50 }
51}
52
53?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/is_scalar.php b/update/plugin/subversion/trunk/xmlrpc/is_scalar.php new file mode 100644 index 0000000..c8f2bfc --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/is_scalar.php
@@ -0,0 +1,38 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15//
16// $Id: is_scalar.php,v 1.2 2005/11/21 10:57:23 ggiunta Exp $
17
18
19/**
20 * Replace is_scalar()
21 *
22 * @category PHP
23 * @package PHP_Compat
24 * @link http://php.net/function.is_scalar
25 * @author Gaetano Giunta
26 * @version $Revision: 1.2 $
27 * @since PHP 4.0.5
28 * @require PHP 4 (is_bool)
29 */
30if (!function_exists('is_scalar')) {
31 function is_scalar($val)
32 {
33 // Check input
34 return (is_bool($val) || is_int($val) || is_float($val) || is_string($val));
35 }
36}
37
38?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/var_export.php b/update/plugin/subversion/trunk/xmlrpc/var_export.php new file mode 100644 index 0000000..3a5ac3f --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/var_export.php
@@ -0,0 +1,105 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: var_export.php,v 1.2 2005/11/21 10:57:23 ggiunta Exp $
19
20
21/**
22 * Replace var_export()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.var_export
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.2 $
29 * @since PHP 4.2.0
30 * @require PHP 4.0.0 (user_error)
31 */
32if (!function_exists('var_export')) {
33 function var_export($array, $return = false, $lvl=0)
34 {
35 // Common output variables
36 $indent = ' ';
37 $doublearrow = ' => ';
38 $lineend = ",\n";
39 $stringdelim = '\'';
40
41 // Check the export isn't a simple string / int
42 if (is_string($array)) {
43 $out = $stringdelim . str_replace('\'', '\\\'', str_replace('\\', '\\\\', $array)) . $stringdelim;
44 } elseif (is_int($array) || is_float($array)) {
45 $out = (string)$array;
46 } elseif (is_bool($array)) {
47 $out = $array ? 'true' : 'false';
48 } elseif (is_null($array)) {
49 $out = 'NULL';
50 } elseif (is_resource($array)) {
51 $out = 'resource';
52 } else {
53 // Begin the array export
54 // Start the string
55 $out = "array (\n";
56
57 // Loop through each value in array
58 foreach ($array as $key => $value) {
59 // If the key is a string, delimit it
60 if (is_string($key)) {
61 $key = str_replace('\'', '\\\'', str_replace('\\', '\\\\', $key));
62 $key = $stringdelim . $key . $stringdelim;
63 }
64
65 $val = var_export($value, true, $lvl+1);
66 // Delimit value
67 /*if (is_array($value)) {
68 // We have an array, so do some recursion
69 // Do some basic recursion while increasing the indent
70 $recur_array = explode($newline, var_export($value, true));
71 $temp_array = array();
72 foreach ($recur_array as $recur_line) {
73 $temp_array[] = $indent . $recur_line;
74 }
75 $recur_array = implode($newline, $temp_array);
76 $value = $newline . $recur_array;
77 } elseif (is_null($value)) {
78 $value = 'NULL';
79 } else {
80 $value = str_replace($find, $replace, $value);
81 $value = $stringdelim . $value . $stringdelim;
82 }*/
83
84 // Piece together the line
85 for ($i = 0; $i < $lvl; $i++)
86 $out .= $indent;
87 $out .= $key . $doublearrow . $val . $lineend;
88 }
89
90 // End our string
91 for ($i = 0; $i < $lvl; $i++)
92 $out .= $indent;
93 $out .= ")";
94 }
95
96 // Decide method of output
97 if ($return === true) {
98 return $out;
99 } else {
100 echo $out;
101 return;
102 }
103 }
104}
105?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/version_compare.php b/update/plugin/subversion/trunk/xmlrpc/version_compare.php new file mode 100644 index 0000000..fc3abac --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/version_compare.php
@@ -0,0 +1,179 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Philippe Jausions <Philippe.Jausions@11abacus.com> |
16// | Aidan Lister <aidan@php.net> |
17// +----------------------------------------------------------------------+
18//
19// $Id: version_compare.php,v 1.1 2005/07/11 16:34:36 ggiunta Exp $
20
21
22/**
23 * Replace version_compare()
24 *
25 * @category PHP
26 * @package PHP_Compat
27 * @link http://php.net/function.version_compare
28 * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
29 * @author Aidan Lister <aidan@php.net>
30 * @version $Revision: 1.1 $
31 * @since PHP 4.1.0
32 * @require PHP 4.0.0 (user_error)
33 */
34if (!function_exists('version_compare')) {
35 function version_compare($version1, $version2, $operator = '<')
36 {
37 // Check input
38 if (!is_scalar($version1)) {
39 user_error('version_compare() expects parameter 1 to be string, ' .
40 gettype($version1) . ' given', E_USER_WARNING);
41 return;
42 }
43
44 if (!is_scalar($version2)) {
45 user_error('version_compare() expects parameter 2 to be string, ' .
46 gettype($version2) . ' given', E_USER_WARNING);
47 return;
48 }
49
50 if (!is_scalar($operator)) {
51 user_error('version_compare() expects parameter 3 to be string, ' .
52 gettype($operator) . ' given', E_USER_WARNING);
53 return;
54 }
55
56 // Standardise versions
57 $v1 = explode('.',
58 str_replace('..', '.',
59 preg_replace('/([^0-9\.]+)/', '.$1.',
60 str_replace(array('-', '_', '+'), '.',
61 trim($version1)))));
62
63 $v2 = explode('.',
64 str_replace('..', '.',
65 preg_replace('/([^0-9\.]+)/', '.$1.',
66 str_replace(array('-', '_', '+'), '.',
67 trim($version2)))));
68
69 // Replace empty entries at the start of the array
70 while (empty($v1[0]) && array_shift($v1)) {}
71 while (empty($v2[0]) && array_shift($v2)) {}
72
73 // Release state order
74 // '#' stands for any number
75 $versions = array(
76 'dev' => 0,
77 'alpha' => 1,
78 'a' => 1,
79 'beta' => 2,
80 'b' => 2,
81 'RC' => 3,
82 '#' => 4,
83 'p' => 5,
84 'pl' => 5);
85
86 // Loop through each segment in the version string
87 $compare = 0;
88 for ($i = 0, $x = min(count($v1), count($v2)); $i < $x; $i++) {
89 if ($v1[$i] == $v2[$i]) {
90 continue;
91 }
92 $i1 = $v1[$i];
93 $i2 = $v2[$i];
94 if (is_numeric($i1) && is_numeric($i2)) {
95 $compare = ($i1 < $i2) ? -1 : 1;
96 break;
97 }
98 // We use the position of '#' in the versions list
99 // for numbers... (so take care of # in original string)
100 if ($i1 == '#') {
101 $i1 = '';
102 } elseif (is_numeric($i1)) {
103 $i1 = '#';
104 }
105 if ($i2 == '#') {
106 $i2 = '';
107 } elseif (is_numeric($i2)) {
108 $i2 = '#';
109 }
110 if (isset($versions[$i1]) && isset($versions[$i2])) {
111 $compare = ($versions[$i1] < $versions[$i2]) ? -1 : 1;
112 } elseif (isset($versions[$i1])) {
113 $compare = 1;
114 } elseif (isset($versions[$i2])) {
115 $compare = -1;
116 } else {
117 $compare = 0;
118 }
119
120 break;
121 }
122
123 // If previous loop didn't find anything, compare the "extra" segments
124 if ($compare == 0) {
125 if (count($v2) > count($v1)) {
126 if (isset($versions[$v2[$i]])) {
127 $compare = ($versions[$v2[$i]] < 4) ? 1 : -1;
128 } else {
129 $compare = -1;
130 }
131 } elseif (count($v2) < count($v1)) {
132 if (isset($versions[$v1[$i]])) {
133 $compare = ($versions[$v1[$i]] < 4) ? -1 : 1;
134 } else {
135 $compare = 1;
136 }
137 }
138 }
139
140 // Compare the versions
141 if (func_num_args() > 2) {
142 switch ($operator) {
143 case '>':
144 case 'gt':
145 return (bool) ($compare > 0);
146 break;
147 case '>=':
148 case 'ge':
149 return (bool) ($compare >= 0);
150 break;
151 case '<=':
152 case 'le':
153 return (bool) ($compare <= 0);
154 break;
155 case '==':
156 case '=':
157 case 'eq':
158 return (bool) ($compare == 0);
159 break;
160 case '<>':
161 case '!=':
162 case 'ne':
163 return (bool) ($compare != 0);
164 break;
165 case '':
166 case '<':
167 case 'lt':
168 return (bool) ($compare < 0);
169 break;
170 default:
171 return;
172 }
173 }
174
175 return $compare;
176 }
177}
178
179?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/xmlrpc.inc b/update/plugin/subversion/trunk/xmlrpc/xmlrpc.inc new file mode 100644 index 0000000..06c6e44 --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/xmlrpc.inc
@@ -0,0 +1,3705 @@
1<?php
2// by Edd Dumbill (C) 1999-2002
3// <edd@usefulinc.com>
4// $Id: xmlrpc.inc,v 1.169 2008/03/06 18:47:24 ggiunta Exp $
5
6// Copyright (c) 1999,2000,2002 Edd Dumbill.
7// All rights reserved.
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions
11// are met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// * Redistributions in binary form must reproduce the above
17// copyright notice, this list of conditions and the following
18// disclaimer in the documentation and/or other materials provided
19// with the distribution.
20//
21// * Neither the name of the "XML-RPC for PHP" nor the names of its
22// contributors may be used to endorse or promote products derived
23// from this software without specific prior written permission.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36// OF THE POSSIBILITY OF SUCH DAMAGE.
37
38 if(!function_exists('xml_parser_create'))
39 {
40 // For PHP 4 onward, XML functionality is always compiled-in on windows:
41 // no more need to dl-open it. It might have been compiled out on *nix...
42 if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43 {
44 dl('xml.so');
45 }
46 }
47
48 // Try to be backward compat with php < 4.2 (are we not being nice ?)
49 $phpversion = phpversion();
50 if($phpversion[0] == '4' && $phpversion[2] < 2)
51 {
52 // give an opportunity to user to specify where to include other files from
53 if(!defined('PHP_XMLRPC_COMPAT_DIR'))
54 {
55 define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
56 }
57 if($phpversion[2] == '0')
58 {
59 if($phpversion[4] < 6)
60 {
61 include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php');
62 }
63 include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php');
64 include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php');
65 include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php');
66 }
67 include(PHP_XMLRPC_COMPAT_DIR.'var_export.php');
68 include(PHP_XMLRPC_COMPAT_DIR.'is_a.php');
69 }
70
71 // G. Giunta 2005/01/29: declare global these variables,
72 // so that xmlrpc.inc will work even if included from within a function
73 // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
74 $GLOBALS['xmlrpcI4']='i4';
75 $GLOBALS['xmlrpcInt']='int';
76 $GLOBALS['xmlrpcBoolean']='boolean';
77 $GLOBALS['xmlrpcDouble']='double';
78 $GLOBALS['xmlrpcString']='string';
79 $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
80 $GLOBALS['xmlrpcBase64']='base64';
81 $GLOBALS['xmlrpcArray']='array';
82 $GLOBALS['xmlrpcStruct']='struct';
83 $GLOBALS['xmlrpcValue']='undefined';
84
85 $GLOBALS['xmlrpcTypes']=array(
86 $GLOBALS['xmlrpcI4'] => 1,
87 $GLOBALS['xmlrpcInt'] => 1,
88 $GLOBALS['xmlrpcBoolean'] => 1,
89 $GLOBALS['xmlrpcString'] => 1,
90 $GLOBALS['xmlrpcDouble'] => 1,
91 $GLOBALS['xmlrpcDateTime'] => 1,
92 $GLOBALS['xmlrpcBase64'] => 1,
93 $GLOBALS['xmlrpcArray'] => 2,
94 $GLOBALS['xmlrpcStruct'] => 3
95 );
96
97 $GLOBALS['xmlrpc_valid_parents'] = array(
98 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
99 'BOOLEAN' => array('VALUE'),
100 'I4' => array('VALUE'),
101 'INT' => array('VALUE'),
102 'STRING' => array('VALUE'),
103 'DOUBLE' => array('VALUE'),
104 'DATETIME.ISO8601' => array('VALUE'),
105 'BASE64' => array('VALUE'),
106 'MEMBER' => array('STRUCT'),
107 'NAME' => array('MEMBER'),
108 'DATA' => array('ARRAY'),
109 'ARRAY' => array('VALUE'),
110 'STRUCT' => array('VALUE'),
111 'PARAM' => array('PARAMS'),
112 'METHODNAME' => array('METHODCALL'),
113 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
114 'FAULT' => array('METHODRESPONSE'),
115 'NIL' => array('VALUE') // only used when extension activated
116 );
117
118 // define extra types for supporting NULL (useful for json or <NIL/>)
119 $GLOBALS['xmlrpcNull']='null';
120 $GLOBALS['xmlrpcTypes']['null']=1;
121
122 // Not in use anymore since 2.0. Shall we remove it?
123 /// @deprecated
124 $GLOBALS['xmlEntities']=array(
125 'amp' => '&',
126 'quot' => '"',
127 'lt' => '<',
128 'gt' => '>',
129 'apos' => "'"
130 );
131
132 // tables used for transcoding different charsets into us-ascii xml
133
134 $GLOBALS['xml_iso88591_Entities']=array();
135 $GLOBALS['xml_iso88591_Entities']['in'] = array();
136 $GLOBALS['xml_iso88591_Entities']['out'] = array();
137 for ($i = 0; $i < 32; $i++)
138 {
139 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
140 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
141 }
142 for ($i = 160; $i < 256; $i++)
143 {
144 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
145 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
146 }
147
148 /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
149 /// These will NOT be present in true ISO-8859-1, but will save the unwary
150 /// windows user from sending junk (though no luck when reciving them...)
151 /*
152 $GLOBALS['xml_cp1252_Entities']=array();
153 for ($i = 128; $i < 160; $i++)
154 {
155 $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
156 }
157 $GLOBALS['xml_cp1252_Entities']['out'] = array(
158 '&#x20AC;', '?', '&#x201A;', '&#x0192;',
159 '&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
160 '&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
161 '&#x0152;', '?', '&#x017D;', '?',
162 '?', '&#x2018;', '&#x2019;', '&#x201C;',
163 '&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
164 '&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
165 '&#x0153;', '?', '&#x017E;', '&#x0178;'
166 );
167 */
168
169 $GLOBALS['xmlrpcerr'] = array(
170 'unknown_method'=>1,
171 'invalid_return'=>2,
172 'incorrect_params'=>3,
173 'introspect_unknown'=>4,
174 'http_error'=>5,
175 'no_data'=>6,
176 'no_ssl'=>7,
177 'curl_fail'=>8,
178 'invalid_request'=>15,
179 'no_curl'=>16,
180 'server_error'=>17,
181 'multicall_error'=>18,
182 'multicall_notstruct'=>9,
183 'multicall_nomethod'=>10,
184 'multicall_notstring'=>11,
185 'multicall_recursion'=>12,
186 'multicall_noparams'=>13,
187 'multicall_notarray'=>14,
188
189 'cannot_decompress'=>103,
190 'decompress_fail'=>104,
191 'dechunk_fail'=>105,
192 'server_cannot_decompress'=>106,
193 'server_decompress_fail'=>107
194 );
195
196 $GLOBALS['xmlrpcstr'] = array(
197 'unknown_method'=>'Unknown method',
198 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
199 'incorrect_params'=>'Incorrect parameters passed to method',
200 'introspect_unknown'=>"Can't introspect: method unknown",
201 'http_error'=>"Didn't receive 200 OK from remote server.",
202 'no_data'=>'No data received from server.',
203 'no_ssl'=>'No SSL support compiled in.',
204 'curl_fail'=>'CURL error',
205 'invalid_request'=>'Invalid request payload',
206 'no_curl'=>'No CURL support compiled in.',
207 'server_error'=>'Internal server error',
208 'multicall_error'=>'Received from server invalid multicall response',
209 'multicall_notstruct'=>'system.multicall expected struct',
210 'multicall_nomethod'=>'missing methodName',
211 'multicall_notstring'=>'methodName is not a string',
212 'multicall_recursion'=>'recursive system.multicall forbidden',
213 'multicall_noparams'=>'missing params',
214 'multicall_notarray'=>'params is not an array',
215
216 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
217 'decompress_fail'=>'Received from server invalid compressed HTTP',
218 'dechunk_fail'=>'Received from server invalid chunked HTTP',
219 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
220 'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
221 );
222
223 // The charset encoding used by the server for received messages and
224 // by the client for received responses when received charset cannot be determined
225 // or is not supported
226 $GLOBALS['xmlrpc_defencoding']='UTF-8';
227
228 // The encoding used internally by PHP.
229 // String values received as xml will be converted to this, and php strings will be converted to xml
230 // as if having been coded with this
231 $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
232
233 $GLOBALS['xmlrpcName']='XML-RPC for PHP';
234 $GLOBALS['xmlrpcVersion']='2.2.1';
235
236 // let user errors start at 800
237 $GLOBALS['xmlrpcerruser']=800;
238 // let XML parse errors start at 100
239 $GLOBALS['xmlrpcerrxml']=100;
240
241 // formulate backslashes for escaping regexp
242 // Not in use anymore since 2.0. Shall we remove it?
243 /// @deprecated
244 $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
245
246 // set to TRUE to enable correct decoding of <NIL/> values
247 $GLOBALS['xmlrpc_null_extension']=false;
248
249 // used to store state during parsing
250 // quick explanation of components:
251 // ac - used to accumulate values
252 // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
253 // isf_reason - used for storing xmlrpcresp fault string
254 // lv - used to indicate "looking for a value": implements
255 // the logic to allow values with no types to be strings
256 // params - used to store parameters in method calls
257 // method - used to store method name
258 // stack - array with genealogy of xml elements names:
259 // used to validate nesting of xmlrpc elements
260 $GLOBALS['_xh']=null;
261
262 /**
263 * Convert a string to the correct XML representation in a target charset
264 * To help correct communication of non-ascii chars inside strings, regardless
265 * of the charset used when sending requests, parsing them, sending responses
266 * and parsing responses, an option is to convert all non-ascii chars present in the message
267 * into their equivalent 'charset entity'. Charset entities enumerated this way
268 * are independent of the charset encoding used to transmit them, and all XML
269 * parsers are bound to understand them.
270 * Note that in the std case we are not sending a charset encoding mime type
271 * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
272 *
273 * @todo do a bit of basic benchmarking (strtr vs. str_replace)
274 * @todo make usage of iconv() or recode_string() or mb_string() where available
275 */
276 function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
277 {
278 if ($src_encoding == '')
279 {
280 // lame, but we know no better...
281 $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
282 }
283
284 switch(strtoupper($src_encoding.'_'.$dest_encoding))
285 {
286 case 'ISO-8859-1_':
287 case 'ISO-8859-1_US-ASCII':
288 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
289 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
290 break;
291 case 'ISO-8859-1_UTF-8':
292 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
293 $escaped_data = utf8_encode($escaped_data);
294 break;
295 case 'ISO-8859-1_ISO-8859-1':
296 case 'US-ASCII_US-ASCII':
297 case 'US-ASCII_UTF-8':
298 case 'US-ASCII_':
299 case 'US-ASCII_ISO-8859-1':
300 case 'UTF-8_UTF-8':
301 //case 'CP1252_CP1252':
302 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
303 break;
304 case 'UTF-8_':
305 case 'UTF-8_US-ASCII':
306 case 'UTF-8_ISO-8859-1':
307 // NB: this will choke on invalid UTF-8, going most likely beyond EOF
308 $escaped_data = '';
309 // be kind to users creating string xmlrpcvals out of different php types
310 $data = (string) $data;
311 $ns = strlen ($data);
312 for ($nn = 0; $nn < $ns; $nn++)
313 {
314 $ch = $data[$nn];
315 $ii = ord($ch);
316 //1 7 0bbbbbbb (127)
317 if ($ii < 128)
318 {
319 /// @todo shall we replace this with a (supposedly) faster str_replace?
320 switch($ii){
321 case 34:
322 $escaped_data .= '&quot;';
323 break;
324 case 38:
325 $escaped_data .= '&amp;';
326 break;
327 case 39:
328 $escaped_data .= '&apos;';
329 break;
330 case 60:
331 $escaped_data .= '&lt;';
332 break;
333 case 62:
334 $escaped_data .= '&gt;';
335 break;
336 default:
337 $escaped_data .= $ch;
338 } // switch
339 }
340 //2 11 110bbbbb 10bbbbbb (2047)
341 else if ($ii>>5 == 6)
342 {
343 $b1 = ($ii & 31);
344 $ii = ord($data[$nn+1]);
345 $b2 = ($ii & 63);
346 $ii = ($b1 * 64) + $b2;
347 $ent = sprintf ('&#%d;', $ii);
348 $escaped_data .= $ent;
349 $nn += 1;
350 }
351 //3 16 1110bbbb 10bbbbbb 10bbbbbb
352 else if ($ii>>4 == 14)
353 {
354 $b1 = ($ii & 31);
355 $ii = ord($data[$nn+1]);
356 $b2 = ($ii & 63);
357 $ii = ord($data[$nn+2]);
358 $b3 = ($ii & 63);
359 $ii = ((($b1 * 64) + $b2) * 64) + $b3;
360 $ent = sprintf ('&#%d;', $ii);
361 $escaped_data .= $ent;
362 $nn += 2;
363 }
364 //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
365 else if ($ii>>3 == 30)
366 {
367 $b1 = ($ii & 31);
368 $ii = ord($data[$nn+1]);
369 $b2 = ($ii & 63);
370 $ii = ord($data[$nn+2]);
371 $b3 = ($ii & 63);
372 $ii = ord($data[$nn+3]);
373 $b4 = ($ii & 63);
374 $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
375 $ent = sprintf ('&#%d;', $ii);
376 $escaped_data .= $ent;
377 $nn += 3;
378 }
379 }
380 break;
381/*
382 case 'CP1252_':
383 case 'CP1252_US-ASCII':
384 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
385 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
386 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
387 break;
388 case 'CP1252_UTF-8':
389 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
390 /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
391 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
392 $escaped_data = utf8_encode($escaped_data);
393 break;
394 case 'CP1252_ISO-8859-1':
395 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
396 // we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
397 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
398 break;
399*/
400 default:
401 $escaped_data = '';
402 error_log("Converting from $src_encoding to $dest_encoding: not supported...");
403 }
404 return $escaped_data;
405 }
406
407 /// xml parser handler function for opening element tags
408 function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
409 {
410 // if invalid xmlrpc already detected, skip all processing
411 if ($GLOBALS['_xh']['isf'] < 2)
412 {
413 // check for correct element nesting
414 // top level element can only be of 2 types
415 /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
416 /// there is only a single top level element in xml anyway
417 if (count($GLOBALS['_xh']['stack']) == 0)
418 {
419 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
420 $name != 'VALUE' && !$accept_single_vals))
421 {
422 $GLOBALS['_xh']['isf'] = 2;
423 $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
424 return;
425 }
426 else
427 {
428 $GLOBALS['_xh']['rt'] = strtolower($name);
429 }
430 }
431 else
432 {
433 // not top level element: see if parent is OK
434 $parent = end($GLOBALS['_xh']['stack']);
435 if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
436 {
437 $GLOBALS['_xh']['isf'] = 2;
438 $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
439 return;
440 }
441 }
442
443 switch($name)
444 {
445 // optimize for speed switch cases: most common cases first
446 case 'VALUE':
447 /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
448 $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
449 $GLOBALS['_xh']['ac']='';
450 $GLOBALS['_xh']['lv']=1;
451 $GLOBALS['_xh']['php_class']=null;
452 break;
453 case 'I4':
454 case 'INT':
455 case 'STRING':
456 case 'BOOLEAN':
457 case 'DOUBLE':
458 case 'DATETIME.ISO8601':
459 case 'BASE64':
460 if ($GLOBALS['_xh']['vt']!='value')
461 {
462 //two data elements inside a value: an error occurred!
463 $GLOBALS['_xh']['isf'] = 2;
464 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
465 return;
466 }
467 $GLOBALS['_xh']['ac']=''; // reset the accumulator
468 break;
469 case 'STRUCT':
470 case 'ARRAY':
471 if ($GLOBALS['_xh']['vt']!='value')
472 {
473 //two data elements inside a value: an error occurred!
474 $GLOBALS['_xh']['isf'] = 2;
475 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
476 return;
477 }
478 // create an empty array to hold child values, and push it onto appropriate stack
479 $cur_val = array();
480 $cur_val['values'] = array();
481 $cur_val['type'] = $name;
482 // check for out-of-band information to rebuild php objs
483 // and in case it is found, save it
484 if (@isset($attrs['PHP_CLASS']))
485 {
486 $cur_val['php_class'] = $attrs['PHP_CLASS'];
487 }
488 $GLOBALS['_xh']['valuestack'][] = $cur_val;
489 $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
490 break;
491 case 'DATA':
492 if ($GLOBALS['_xh']['vt']!='data')
493 {
494 //two data elements inside a value: an error occurred!
495 $GLOBALS['_xh']['isf'] = 2;
496 $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
497 return;
498 }
499 case 'METHODCALL':
500 case 'METHODRESPONSE':
501 case 'PARAMS':
502 // valid elements that add little to processing
503 break;
504 case 'METHODNAME':
505 case 'NAME':
506 /// @todo we could check for 2 NAME elements inside a MEMBER element
507 $GLOBALS['_xh']['ac']='';
508 break;
509 case 'FAULT':
510 $GLOBALS['_xh']['isf']=1;
511 break;
512 case 'MEMBER':
513 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
514 //$GLOBALS['_xh']['ac']='';
515 // Drop trough intentionally
516 case 'PARAM':
517 // clear value type, so we can check later if no value has been passed for this param/member
518 $GLOBALS['_xh']['vt']=null;
519 break;
520 case 'NIL':
521 if ($GLOBALS['xmlrpc_null_extension'])
522 {
523 if ($GLOBALS['_xh']['vt']!='value')
524 {
525 //two data elements inside a value: an error occurred!
526 $GLOBALS['_xh']['isf'] = 2;
527 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
528 return;
529 }
530 $GLOBALS['_xh']['ac']=''; // reset the accumulator
531 break;
532 }
533 // we do not support the <NIL/> extension, so
534 // drop through intentionally
535 default:
536 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
537 $GLOBALS['_xh']['isf'] = 2;
538 $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
539 break;
540 }
541
542 // Save current element name to stack, to validate nesting
543 $GLOBALS['_xh']['stack'][] = $name;
544
545 /// @todo optimization creep: move this inside the big switch() above
546 if($name!='VALUE')
547 {
548 $GLOBALS['_xh']['lv']=0;
549 }
550 }
551 }
552
553 /// Used in decoding xml chunks that might represent single xmlrpc values
554 function xmlrpc_se_any($parser, $name, $attrs)
555 {
556 xmlrpc_se($parser, $name, $attrs, true);
557 }
558
559 /// xml parser handler function for close element tags
560 function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
561 {
562 if ($GLOBALS['_xh']['isf'] < 2)
563 {
564 // push this element name from stack
565 // NB: if XML validates, correct opening/closing is guaranteed and
566 // we do not have to check for $name == $curr_elem.
567 // we also checked for proper nesting at start of elements...
568 $curr_elem = array_pop($GLOBALS['_xh']['stack']);
569
570 switch($name)
571 {
572 case 'VALUE':
573 // This if() detects if no scalar was inside <VALUE></VALUE>
574 if ($GLOBALS['_xh']['vt']=='value')
575 {
576 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
577 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
578 }
579
580 if ($rebuild_xmlrpcvals)
581 {
582 // build the xmlrpc val out of the data received, and substitute it
583 $temp =& new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
584 // in case we got info about underlying php class, save it
585 // in the object we're rebuilding
586 if (isset($GLOBALS['_xh']['php_class']))
587 $temp->_php_class = $GLOBALS['_xh']['php_class'];
588 // check if we are inside an array or struct:
589 // if value just built is inside an array, let's move it into array on the stack
590 $vscount = count($GLOBALS['_xh']['valuestack']);
591 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
592 {
593 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
594 }
595 else
596 {
597 $GLOBALS['_xh']['value'] = $temp;
598 }
599 }
600 else
601 {
602 /// @todo this needs to treat correctly php-serialized objects,
603 /// since std deserializing is done by php_xmlrpc_decode,
604 /// which we will not be calling...
605 if (isset($GLOBALS['_xh']['php_class']))
606 {
607 }
608
609 // check if we are inside an array or struct:
610 // if value just built is inside an array, let's move it into array on the stack
611 $vscount = count($GLOBALS['_xh']['valuestack']);
612 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
613 {
614 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
615 }
616 }
617 break;
618 case 'BOOLEAN':
619 case 'I4':
620 case 'INT':
621 case 'STRING':
622 case 'DOUBLE':
623 case 'DATETIME.ISO8601':
624 case 'BASE64':
625 $GLOBALS['_xh']['vt']=strtolower($name);
626 /// @todo: optimization creep - remove the if/elseif cycle below
627 /// since the case() in which we are already did that
628 if ($name=='STRING')
629 {
630 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
631 }
632 elseif ($name=='DATETIME.ISO8601')
633 {
634 if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
635 {
636 error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
637 }
638 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
639 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
640 }
641 elseif ($name=='BASE64')
642 {
643 /// @todo check for failure of base64 decoding / catch warnings
644 $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
645 }
646 elseif ($name=='BOOLEAN')
647 {
648 // special case here: we translate boolean 1 or 0 into PHP
649 // constants true or false.
650 // Strings 'true' and 'false' are accepted, even though the
651 // spec never mentions them (see eg. Blogger api docs)
652 // NB: this simple checks helps a lot sanitizing input, ie no
653 // security problems around here
654 if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
655 {
656 $GLOBALS['_xh']['value']=true;
657 }
658 else
659 {
660 // log if receiveing something strange, even though we set the value to false anyway
661 if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($_xh[$parser]['ac'], 'false') != 0)
662 error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
663 $GLOBALS['_xh']['value']=false;
664 }
665 }
666 elseif ($name=='DOUBLE')
667 {
668 // we have a DOUBLE
669 // we must check that only 0123456789-.<space> are characters here
670 // NOTE: regexp could be much stricter than this...
671 if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
672 {
673 /// @todo: find a better way of throwing an error than this!
674 error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
675 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
676 }
677 else
678 {
679 // it's ok, add it on
680 $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
681 }
682 }
683 else
684 {
685 // we have an I4/INT
686 // we must check that only 0123456789-<space> are characters here
687 if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
688 {
689 /// @todo find a better way of throwing an error than this!
690 error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
691 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
692 }
693 else
694 {
695 // it's ok, add it on
696 $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
697 }
698 }
699 //$GLOBALS['_xh']['ac']=''; // is this necessary?
700 $GLOBALS['_xh']['lv']=3; // indicate we've found a value
701 break;
702 case 'NAME':
703 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
704 break;
705 case 'MEMBER':
706 //$GLOBALS['_xh']['ac']=''; // is this necessary?
707 // add to array in the stack the last element built,
708 // unless no VALUE was found
709 if ($GLOBALS['_xh']['vt'])
710 {
711 $vscount = count($GLOBALS['_xh']['valuestack']);
712 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
713 } else
714 error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
715 break;
716 case 'DATA':
717 //$GLOBALS['_xh']['ac']=''; // is this necessary?
718 $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
719 break;
720 case 'STRUCT':
721 case 'ARRAY':
722 // fetch out of stack array of values, and promote it to current value
723 $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
724 $GLOBALS['_xh']['value'] = $curr_val['values'];
725 $GLOBALS['_xh']['vt']=strtolower($name);
726 if (isset($curr_val['php_class']))
727 {
728 $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
729 }
730 break;
731 case 'PARAM':
732 // add to array of params the current value,
733 // unless no VALUE was found
734 if ($GLOBALS['_xh']['vt'])
735 {
736 $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
737 $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
738 }
739 else
740 error_log('XML-RPC: missing VALUE inside PARAM in received xml');
741 break;
742 case 'METHODNAME':
743 $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
744 break;
745 case 'NIL':
746 if ($GLOBALS['xmlrpc_null_extension'])
747 {
748 $GLOBALS['_xh']['vt']='null';
749 $GLOBALS['_xh']['value']=null;
750 $GLOBALS['_xh']['lv']=3;
751 break;
752 }
753 // drop through intentionally if nil extension not enabled
754 case 'PARAMS':
755 case 'FAULT':
756 case 'METHODCALL':
757 case 'METHORESPONSE':
758 break;
759 default:
760 // End of INVALID ELEMENT!
761 // shall we add an assert here for unreachable code???
762 break;
763 }
764 }
765 }
766
767 /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
768 function xmlrpc_ee_fast($parser, $name)
769 {
770 xmlrpc_ee($parser, $name, false);
771 }
772
773 /// xml parser handler function for character data
774 function xmlrpc_cd($parser, $data)
775 {
776 // skip processing if xml fault already detected
777 if ($GLOBALS['_xh']['isf'] < 2)
778 {
779 // "lookforvalue==3" means that we've found an entire value
780 // and should discard any further character data
781 if($GLOBALS['_xh']['lv']!=3)
782 {
783 // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
784 //if($GLOBALS['_xh']['lv']==1)
785 //{
786 // if we've found text and we're just in a <value> then
787 // say we've found a value
788 //$GLOBALS['_xh']['lv']=2;
789 //}
790 // we always initialize the accumulator before starting parsing, anyway...
791 //if(!@isset($GLOBALS['_xh']['ac']))
792 //{
793 // $GLOBALS['_xh']['ac'] = '';
794 //}
795 $GLOBALS['_xh']['ac'].=$data;
796 }
797 }
798 }
799
800 /// xml parser handler function for 'other stuff', ie. not char data or
801 /// element start/end tag. In fact it only gets called on unknown entities...
802 function xmlrpc_dh($parser, $data)
803 {
804 // skip processing if xml fault already detected
805 if ($GLOBALS['_xh']['isf'] < 2)
806 {
807 if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
808 {
809 // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
810 //if($GLOBALS['_xh']['lv']==1)
811 //{
812 // $GLOBALS['_xh']['lv']=2;
813 //}
814 $GLOBALS['_xh']['ac'].=$data;
815 }
816 }
817 return true;
818 }
819
820 class xmlrpc_client
821 {
822 var $path;
823 var $server;
824 var $port=0;
825 var $method='http';
826 var $errno;
827 var $errstr;
828 var $debug=0;
829 var $username='';
830 var $password='';
831 var $authtype=1;
832 var $cert='';
833 var $certpass='';
834 var $cacert='';
835 var $cacertdir='';
836 var $key='';
837 var $keypass='';
838 var $verifypeer=true;
839 var $verifyhost=1;
840 var $no_multicall=false;
841 var $proxy='';
842 var $proxyport=0;
843 var $proxy_user='';
844 var $proxy_pass='';
845 var $proxy_authtype=1;
846 var $cookies=array();
847 /**
848 * List of http compression methods accepted by the client for responses.
849 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
850 *
851 * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
852 * in those cases it will be up to CURL to decide the compression methods
853 * it supports. You might check for the presence of 'zlib' in the output of
854 * curl_version() to determine wheter compression is supported or not
855 */
856 var $accepted_compression = array();
857 /**
858 * Name of compression scheme to be used for sending requests.
859 * Either null, gzip or deflate
860 */
861 var $request_compression = '';
862 /**
863 * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
864 * http://curl.haxx.se/docs/faq.html#7.3)
865 */
866 var $xmlrpc_curl_handle = null;
867 /// Wheter to use persistent connections for http 1.1 and https
868 var $keepalive = false;
869 /// Charset encodings that can be decoded without problems by the client
870 var $accepted_charset_encodings = array();
871 /// Charset encoding to be used in serializing request. NULL = use ASCII
872 var $request_charset_encoding = '';
873 /**
874 * Decides the content of xmlrpcresp objects returned by calls to send()
875 * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
876 */
877 var $return_type = 'xmlrpcvals';
878
879 /**
880 * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
881 * @param string $server the server name / ip address
882 * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
883 * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
884 */
885 function xmlrpc_client($path, $server='', $port='', $method='')
886 {
887 // allow user to specify all params in $path
888 if($server == '' and $port == '' and $method == '')
889 {
890 $parts = parse_url($path);
891 $server = $parts['host'];
892 $path = isset($parts['path']) ? $parts['path'] : '';
893 if(isset($parts['query']))
894 {
895 $path .= '?'.$parts['query'];
896 }
897 if(isset($parts['fragment']))
898 {
899 $path .= '#'.$parts['fragment'];
900 }
901 if(isset($parts['port']))
902 {
903 $port = $parts['port'];
904 }
905 if(isset($parts['scheme']))
906 {
907 $method = $parts['scheme'];
908 }
909 if(isset($parts['user']))
910 {
911 $this->username = $parts['user'];
912 }
913 if(isset($parts['pass']))
914 {
915 $this->password = $parts['pass'];
916 }
917 }
918 if($path == '' || $path[0] != '/')
919 {
920 $this->path='/'.$path;
921 }
922 else
923 {
924 $this->path=$path;
925 }
926 $this->server=$server;
927 if($port != '')
928 {
929 $this->port=$port;
930 }
931 if($method != '')
932 {
933 $this->method=$method;
934 }
935
936 // if ZLIB is enabled, let the client by default accept compressed responses
937 if(function_exists('gzinflate') || (
938 function_exists('curl_init') && (($info = curl_version()) &&
939 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
940 ))
941 {
942 $this->accepted_compression = array('gzip', 'deflate');
943 }
944
945 // keepalives: enabled by default ONLY for PHP >= 4.3.8
946 // (see http://curl.haxx.se/docs/faq.html#7.3)
947 if(version_compare(phpversion(), '4.3.8') >= 0)
948 {
949 $this->keepalive = true;
950 }
951
952 // by default the xml parser can support these 3 charset encodings
953 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
954 }
955
956 /**
957 * Enables/disables the echoing to screen of the xmlrpc responses received
958 * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
959 * @access public
960 */
961 function setDebug($in)
962 {
963 $this->debug=$in;
964 }
965
966 /**
967 * Add some http BASIC AUTH credentials, used by the client to authenticate
968 * @param string $u username
969 * @param string $p password
970 * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
971 * @access public
972 */
973 function setCredentials($u, $p, $t=1)
974 {
975 $this->username=$u;
976 $this->password=$p;
977 $this->authtype=$t;
978 }
979
980 /**
981 * Add a client-side https certificate
982 * @param string $cert
983 * @param string $certpass
984 * @access public
985 */
986 function setCertificate($cert, $certpass)
987 {
988 $this->cert = $cert;
989 $this->certpass = $certpass;
990 }
991
992 /**
993 * Add a CA certificate to verify server with (see man page about
994 * CURLOPT_CAINFO for more details
995 * @param string $cacert certificate file name (or dir holding certificates)
996 * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
997 * @access public
998 */
999 function setCaCertificate($cacert, $is_dir=false)
1000 {
1001 if ($is_dir)
1002 {
1003 $this->cacertdir = $cacert;
1004 }
1005 else
1006 {
1007 $this->cacert = $cacert;
1008 }
1009 }
1010
1011 /**
1012 * Set attributes for SSL communication: private SSL key
1013 * NB: does not work in older php/curl installs
1014 * Thanks to Daniel Convissor
1015 * @param string $key The name of a file containing a private SSL key
1016 * @param string $keypass The secret password needed to use the private SSL key
1017 * @access public
1018 */
1019 function setKey($key, $keypass)
1020 {
1021 $this->key = $key;
1022 $this->keypass = $keypass;
1023 }
1024
1025 /**
1026 * Set attributes for SSL communication: verify server certificate
1027 * @param bool $i enable/disable verification of peer certificate
1028 * @access public
1029 */
1030 function setSSLVerifyPeer($i)
1031 {
1032 $this->verifypeer = $i;
1033 }
1034
1035 /**
1036 * Set attributes for SSL communication: verify match of server cert w. hostname
1037 * @param int $i
1038 * @access public
1039 */
1040 function setSSLVerifyHost($i)
1041 {
1042 $this->verifyhost = $i;
1043 }
1044
1045 /**
1046 * Set proxy info
1047 * @param string $proxyhost
1048 * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1049 * @param string $proxyusername Leave blank if proxy has public access
1050 * @param string $proxypassword Leave blank if proxy has public access
1051 * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1052 * @access public
1053 */
1054 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1055 {
1056 $this->proxy = $proxyhost;
1057 $this->proxyport = $proxyport;
1058 $this->proxy_user = $proxyusername;
1059 $this->proxy_pass = $proxypassword;
1060 $this->proxy_authtype = $proxyauthtype;
1061 }
1062
1063 /**
1064 * Enables/disables reception of compressed xmlrpc responses.
1065 * Note that enabling reception of compressed responses merely adds some standard
1066 * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1067 * compressed responses when receiving such requests.
1068 * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1069 * @access public
1070 */
1071 function setAcceptedCompression($compmethod)
1072 {
1073 if ($compmethod == 'any')
1074 $this->accepted_compression = array('gzip', 'deflate');
1075 else
1076 $this->accepted_compression = array($compmethod);
1077 }
1078
1079 /**
1080 * Enables/disables http compression of xmlrpc request.
1081 * Take care when sending compressed requests: servers might not support them
1082 * (and automatic fallback to uncompressed requests is not yet implemented)
1083 * @param string $compmethod either 'gzip', 'deflate' or ''
1084 * @access public
1085 */
1086 function setRequestCompression($compmethod)
1087 {
1088 $this->request_compression = $compmethod;
1089 }
1090
1091 /**
1092 * Adds a cookie to list of cookies that will be sent to server.
1093 * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1094 * do not do it unless you know what you are doing
1095 * @param string $name
1096 * @param string $value
1097 * @param string $path
1098 * @param string $domain
1099 * @param int $port
1100 * @access public
1101 *
1102 * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1103 */
1104 function setCookie($name, $value='', $path='', $domain='', $port=null)
1105 {
1106 $this->cookies[$name]['value'] = urlencode($value);
1107 if ($path || $domain || $port)
1108 {
1109 $this->cookies[$name]['path'] = $path;
1110 $this->cookies[$name]['domain'] = $domain;
1111 $this->cookies[$name]['port'] = $port;
1112 $this->cookies[$name]['version'] = 1;
1113 }
1114 else
1115 {
1116 $this->cookies[$name]['version'] = 0;
1117 }
1118 }
1119
1120 /**
1121 * Send an xmlrpc request
1122 * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1123 * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1124 * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1125 * @return xmlrpcresp
1126 * @access public
1127 */
1128 function& send($msg, $timeout=0, $method='')
1129 {
1130 // if user deos not specify http protocol, use native method of this client
1131 // (i.e. method set during call to constructor)
1132 if($method == '')
1133 {
1134 $method = $this->method;
1135 }
1136
1137 if(is_array($msg))
1138 {
1139 // $msg is an array of xmlrpcmsg's
1140 $r = $this->multicall($msg, $timeout, $method);
1141 return $r;
1142 }
1143 elseif(is_string($msg))
1144 {
1145 $n =& new xmlrpcmsg('');
1146 $n->payload = $msg;
1147 $msg = $n;
1148 }
1149
1150 // where msg is an xmlrpcmsg
1151 $msg->debug=$this->debug;
1152
1153 if($method == 'https')
1154 {
1155 $r =& $this->sendPayloadHTTPS(
1156 $msg,
1157 $this->server,
1158 $this->port,
1159 $timeout,
1160 $this->username,
1161 $this->password,
1162 $this->authtype,
1163 $this->cert,
1164 $this->certpass,
1165 $this->cacert,
1166 $this->cacertdir,
1167 $this->proxy,
1168 $this->proxyport,
1169 $this->proxy_user,
1170 $this->proxy_pass,
1171 $this->proxy_authtype,
1172 $this->keepalive,
1173 $this->key,
1174 $this->keypass
1175 );
1176 }
1177 elseif($method == 'http11')
1178 {
1179 $r =& $this->sendPayloadCURL(
1180 $msg,
1181 $this->server,
1182 $this->port,
1183 $timeout,
1184 $this->username,
1185 $this->password,
1186 $this->authtype,
1187 null,
1188 null,
1189 null,
1190 null,
1191 $this->proxy,
1192 $this->proxyport,
1193 $this->proxy_user,
1194 $this->proxy_pass,
1195 $this->proxy_authtype,
1196 'http',
1197 $this->keepalive
1198 );
1199 }
1200 else
1201 {
1202 $r =& $this->sendPayloadHTTP10(
1203 $msg,
1204 $this->server,
1205 $this->port,
1206 $timeout,
1207 $this->username,
1208 $this->password,
1209 $this->authtype,
1210 $this->proxy,
1211 $this->proxyport,
1212 $this->proxy_user,
1213 $this->proxy_pass,
1214 $this->proxy_authtype
1215 );
1216 }
1217
1218 return $r;
1219 }
1220
1221 /**
1222 * @access private
1223 */
1224 function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1225 $username='', $password='', $authtype=1, $proxyhost='',
1226 $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1227 {
1228 if($port==0)
1229 {
1230 $port=80;
1231 }
1232
1233 // Only create the payload if it was not created previously
1234 if(empty($msg->payload))
1235 {
1236 $msg->createPayload($this->request_charset_encoding);
1237 }
1238
1239 $payload = $msg->payload;
1240 // Deflate request body and set appropriate request headers
1241 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1242 {
1243 if($this->request_compression == 'gzip')
1244 {
1245 $a = @gzencode($payload);
1246 if($a)
1247 {
1248 $payload = $a;
1249 $encoding_hdr = "Content-Encoding: gzip\r\n";
1250 }
1251 }
1252 else
1253 {
1254 $a = @gzcompress($payload);
1255 if($a)
1256 {
1257 $payload = $a;
1258 $encoding_hdr = "Content-Encoding: deflate\r\n";
1259 }
1260 }
1261 }
1262 else
1263 {
1264 $encoding_hdr = '';
1265 }
1266
1267 // thanks to Grant Rauscher <grant7@firstworld.net> for this
1268 $credentials='';
1269 if($username!='')
1270 {
1271 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1272 if ($authtype != 1)
1273 {
1274 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0');
1275 }
1276 }
1277
1278 $accepted_encoding = '';
1279 if(is_array($this->accepted_compression) && count($this->accepted_compression))
1280 {
1281 $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1282 }
1283
1284 $proxy_credentials = '';
1285 if($proxyhost)
1286 {
1287 if($proxyport == 0)
1288 {
1289 $proxyport = 8080;
1290 }
1291 $connectserver = $proxyhost;
1292 $connectport = $proxyport;
1293 $uri = 'http://'.$server.':'.$port.$this->path;
1294 if($proxyusername != '')
1295 {
1296 if ($proxyauthtype != 1)
1297 {
1298 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0');
1299 }
1300 $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1301 }
1302 }
1303 else
1304 {
1305 $connectserver = $server;
1306 $connectport = $port;
1307 $uri = $this->path;
1308 }
1309
1310 // Cookie generation, as per rfc2965 (version 1 cookies) or
1311 // netscape's rules (version 0 cookies)
1312 $cookieheader='';
1313 if (count($this->cookies))
1314 {
1315 $version = '';
1316 foreach ($this->cookies as $name => $cookie)
1317 {
1318 if ($cookie['version'])
1319 {
1320 $version = ' $Version="' . $cookie['version'] . '";';
1321 $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1322 if ($cookie['path'])
1323 $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1324 if ($cookie['domain'])
1325 $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1326 if ($cookie['port'])
1327 $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1328 }
1329 else
1330 {
1331 $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1332 }
1333 }
1334 $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1335 }
1336
1337 $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1338 'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" .
1339 'Host: '. $server . ':' . $port . "\r\n" .
1340 $credentials .
1341 $proxy_credentials .
1342 $accepted_encoding .
1343 $encoding_hdr .
1344 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1345 $cookieheader .
1346 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1347 strlen($payload) . "\r\n\r\n" .
1348 $payload;
1349
1350 if($this->debug > 1)
1351 {
1352 print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1353 // let the client see this now in case http times out...
1354 flush();
1355 }
1356
1357 if($timeout>0)
1358 {
1359 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1360 }
1361 else
1362 {
1363 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1364 }
1365 if($fp)
1366 {
1367 if($timeout>0 && function_exists('stream_set_timeout'))
1368 {
1369 stream_set_timeout($fp, $timeout);
1370 }
1371 }
1372 else
1373 {
1374 $this->errstr='Connect error: '.$this->errstr;
1375 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1376 return $r;
1377 }
1378
1379 if(!fputs($fp, $op, strlen($op)))
1380 {
1381 $this->errstr='Write error';
1382 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1383 return $r;
1384 }
1385 else
1386 {
1387 // reset errno and errstr on succesful socket connection
1388 $this->errstr = '';
1389 }
1390 // G. Giunta 2005/10/24: close socket before parsing.
1391 // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1392 $ipd='';
1393 while($data=fread($fp, 32768))
1394 {
1395 // shall we check for $data === FALSE?
1396 // as per the manual, it signals an error
1397 $ipd.=$data;
1398 }
1399 fclose($fp);
1400 $r =& $msg->parseResponse($ipd, false, $this->return_type);
1401 return $r;
1402
1403 }
1404
1405 /**
1406 * @access private
1407 */
1408 function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1409 $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1410 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1411 $keepalive=false, $key='', $keypass='')
1412 {
1413 $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1414 $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1415 $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1416 return $r;
1417 }
1418
1419 /**
1420 * Contributed by Justin Miller <justin@voxel.net>
1421 * Requires curl to be built into PHP
1422 * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1423 * @access private
1424 */
1425 function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1426 $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1427 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1428 $keepalive=false, $key='', $keypass='')
1429 {
1430 if(!function_exists('curl_init'))
1431 {
1432 $this->errstr='CURL unavailable on this install';
1433 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1434 return $r;
1435 }
1436 if($method == 'https')
1437 {
1438 if(($info = curl_version()) &&
1439 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1440 {
1441 $this->errstr='SSL unavailable on this install';
1442 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1443 return $r;
1444 }
1445 }
1446
1447 if($port == 0)
1448 {
1449 if($method == 'http')
1450 {
1451 $port = 80;
1452 }
1453 else
1454 {
1455 $port = 443;
1456 }
1457 }
1458
1459 // Only create the payload if it was not created previously
1460 if(empty($msg->payload))
1461 {
1462 $msg->createPayload($this->request_charset_encoding);
1463 }
1464
1465 // Deflate request body and set appropriate request headers
1466 $payload = $msg->payload;
1467 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1468 {
1469 if($this->request_compression == 'gzip')
1470 {
1471 $a = @gzencode($payload);
1472 if($a)
1473 {
1474 $payload = $a;
1475 $encoding_hdr = 'Content-Encoding: gzip';
1476 }
1477 }
1478 else
1479 {
1480 $a = @gzcompress($payload);
1481 if($a)
1482 {
1483 $payload = $a;
1484 $encoding_hdr = 'Content-Encoding: deflate';
1485 }
1486 }
1487 }
1488 else
1489 {
1490 $encoding_hdr = '';
1491 }
1492
1493 if($this->debug > 1)
1494 {
1495 print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1496 // let the client see this now in case http times out...
1497 flush();
1498 }
1499
1500 if(!$keepalive || !$this->xmlrpc_curl_handle)
1501 {
1502 $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1503 if($keepalive)
1504 {
1505 $this->xmlrpc_curl_handle = $curl;
1506 }
1507 }
1508 else
1509 {
1510 $curl = $this->xmlrpc_curl_handle;
1511 }
1512
1513 // results into variable
1514 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1515
1516 if($this->debug)
1517 {
1518 curl_setopt($curl, CURLOPT_VERBOSE, 1);
1519 }
1520 curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
1521 // required for XMLRPC: post the data
1522 curl_setopt($curl, CURLOPT_POST, 1);
1523 // the data
1524 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1525
1526 // return the header too
1527 curl_setopt($curl, CURLOPT_HEADER, 1);
1528
1529 // will only work with PHP >= 5.0
1530 // NB: if we set an empty string, CURL will add http header indicating
1531 // ALL methods it is supporting. This is possibly a better option than
1532 // letting the user tell what curl can / cannot do...
1533 if(is_array($this->accepted_compression) && count($this->accepted_compression))
1534 {
1535 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1536 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1537 if (count($this->accepted_compression) == 1)
1538 {
1539 curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1540 }
1541 else
1542 curl_setopt($curl, CURLOPT_ENCODING, '');
1543 }
1544 // extra headers
1545 $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1546 // if no keepalive is wanted, let the server know it in advance
1547 if(!$keepalive)
1548 {
1549 $headers[] = 'Connection: close';
1550 }
1551 // request compression header
1552 if($encoding_hdr)
1553 {
1554 $headers[] = $encoding_hdr;
1555 }
1556
1557 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1558 // timeout is borked
1559 if($timeout)
1560 {
1561 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1562 }
1563
1564 if($username && $password)
1565 {
1566 curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1567 if (defined('CURLOPT_HTTPAUTH'))
1568 {
1569 curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1570 }
1571 else if ($authtype != 1)
1572 {
1573 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install');
1574 }
1575 }
1576
1577 if($method == 'https')
1578 {
1579 // set cert file
1580 if($cert)
1581 {
1582 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1583 }
1584 // set cert password
1585 if($certpass)
1586 {
1587 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1588 }
1589 // whether to verify remote host's cert
1590 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1591 // set ca certificates file/dir
1592 if($cacert)
1593 {
1594 curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1595 }
1596 if($cacertdir)
1597 {
1598 curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1599 }
1600 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1601 if($key)
1602 {
1603 curl_setopt($curl, CURLOPT_SSLKEY, $key);
1604 }
1605 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1606 if($keypass)
1607 {
1608 curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1609 }
1610 // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1611 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1612 }
1613
1614 // proxy info
1615 if($proxyhost)
1616 {
1617 if($proxyport == 0)
1618 {
1619 $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1620 }
1621 curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1622 //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1623 if($proxyusername)
1624 {
1625 curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1626 if (defined('CURLOPT_PROXYAUTH'))
1627 {
1628 curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1629 }
1630 else if ($proxyauthtype != 1)
1631 {
1632 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1633 }
1634 }
1635 }
1636
1637 // NB: should we build cookie http headers by hand rather than let CURL do it?
1638 // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1639 // set to client obj the the user...
1640 if (count($this->cookies))
1641 {
1642 $cookieheader = '';
1643 foreach ($this->cookies as $name => $cookie)
1644 {
1645 $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1646 }
1647 curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1648 }
1649
1650 $result = curl_exec($curl);
1651
1652 if ($this->debug > 1)
1653 {
1654 print "<PRE>\n---CURL INFO---\n";
1655 foreach(curl_getinfo($curl) as $name => $val)
1656 print $name . ': ' . htmlentities($val). "\n";
1657 print "---END---\n</PRE>";
1658 }
1659
1660 if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1661 {
1662 $this->errstr='no response';
1663 $resp=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1664 curl_close($curl);
1665 if($keepalive)
1666 {
1667 $this->xmlrpc_curl_handle = null;
1668 }
1669 }
1670 else
1671 {
1672 if(!$keepalive)
1673 {
1674 curl_close($curl);
1675 }
1676 $resp =& $msg->parseResponse($result, true, $this->return_type);
1677 }
1678 return $resp;
1679 }
1680
1681 /**
1682 * Send an array of request messages and return an array of responses.
1683 * Unless $this->no_multicall has been set to true, it will try first
1684 * to use one single xmlrpc call to server method system.multicall, and
1685 * revert to sending many successive calls in case of failure.
1686 * This failure is also stored in $this->no_multicall for subsequent calls.
1687 * Unfortunately, there is no server error code universally used to denote
1688 * the fact that multicall is unsupported, so there is no way to reliably
1689 * distinguish between that and a temporary failure.
1690 * If you are sure that server supports multicall and do not want to
1691 * fallback to using many single calls, set the fourth parameter to FALSE.
1692 *
1693 * NB: trying to shoehorn extra functionality into existing syntax has resulted
1694 * in pretty much convoluted code...
1695 *
1696 * @param array $msgs an array of xmlrpcmsg objects
1697 * @param integer $timeout connection timeout (in seconds)
1698 * @param string $method the http protocol variant to be used
1699 * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1700 * @return array
1701 * @access public
1702 */
1703 function multicall($msgs, $timeout=0, $method='', $fallback=true)
1704 {
1705 if ($method == '')
1706 {
1707 $method = $this->method;
1708 }
1709 if(!$this->no_multicall)
1710 {
1711 $results = $this->_try_multicall($msgs, $timeout, $method);
1712 if(is_array($results))
1713 {
1714 // System.multicall succeeded
1715 return $results;
1716 }
1717 else
1718 {
1719 // either system.multicall is unsupported by server,
1720 // or call failed for some other reason.
1721 if ($fallback)
1722 {
1723 // Don't try it next time...
1724 $this->no_multicall = true;
1725 }
1726 else
1727 {
1728 if (is_a($results, 'xmlrpcresp'))
1729 {
1730 $result = $results;
1731 }
1732 else
1733 {
1734 $result =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1735 }
1736 }
1737 }
1738 }
1739 else
1740 {
1741 // override fallback, in case careless user tries to do two
1742 // opposite things at the same time
1743 $fallback = true;
1744 }
1745
1746 $results = array();
1747 if ($fallback)
1748 {
1749 // system.multicall is (probably) unsupported by server:
1750 // emulate multicall via multiple requests
1751 foreach($msgs as $msg)
1752 {
1753 $results[] =& $this->send($msg, $timeout, $method);
1754 }
1755 }
1756 else
1757 {
1758 // user does NOT want to fallback on many single calls:
1759 // since we should always return an array of responses,
1760 // return an array with the same error repeated n times
1761 foreach($msgs as $msg)
1762 {
1763 $results[] = $result;
1764 }
1765 }
1766 return $results;
1767 }
1768
1769 /**
1770 * Attempt to boxcar $msgs via system.multicall.
1771 * Returns either an array of xmlrpcreponses, an xmlrpc error response
1772 * or false (when received response does not respect valid multicall syntax)
1773 * @access private
1774 */
1775 function _try_multicall($msgs, $timeout, $method)
1776 {
1777 // Construct multicall message
1778 $calls = array();
1779 foreach($msgs as $msg)
1780 {
1781 $call['methodName'] =& new xmlrpcval($msg->method(),'string');
1782 $numParams = $msg->getNumParams();
1783 $params = array();
1784 for($i = 0; $i < $numParams; $i++)
1785 {
1786 $params[$i] = $msg->getParam($i);
1787 }
1788 $call['params'] =& new xmlrpcval($params, 'array');
1789 $calls[] =& new xmlrpcval($call, 'struct');
1790 }
1791 $multicall =& new xmlrpcmsg('system.multicall');
1792 $multicall->addParam(new xmlrpcval($calls, 'array'));
1793
1794 // Attempt RPC call
1795 $result =& $this->send($multicall, $timeout, $method);
1796
1797 if($result->faultCode() != 0)
1798 {
1799 // call to system.multicall failed
1800 return $result;
1801 }
1802
1803 // Unpack responses.
1804 $rets = $result->value();
1805
1806 if ($this->return_type == 'xml')
1807 {
1808 return $rets;
1809 }
1810 else if ($this->return_type == 'phpvals')
1811 {
1812 ///@todo test this code branch...
1813 $rets = $result->value();
1814 if(!is_array($rets))
1815 {
1816 return false; // bad return type from system.multicall
1817 }
1818 $numRets = count($rets);
1819 if($numRets != count($msgs))
1820 {
1821 return false; // wrong number of return values.
1822 }
1823
1824 $response = array();
1825 for($i = 0; $i < $numRets; $i++)
1826 {
1827 $val = $rets[$i];
1828 if (!is_array($val)) {
1829 return false;
1830 }
1831 switch(count($val))
1832 {
1833 case 1:
1834 if(!isset($val[0]))
1835 {
1836 return false; // Bad value
1837 }
1838 // Normal return value
1839 $response[$i] =& new xmlrpcresp($val[0], 0, '', 'phpvals');
1840 break;
1841 case 2:
1842 /// @todo remove usage of @: it is apparently quite slow
1843 $code = @$val['faultCode'];
1844 if(!is_int($code))
1845 {
1846 return false;
1847 }
1848 $str = @$val['faultString'];
1849 if(!is_string($str))
1850 {
1851 return false;
1852 }
1853 $response[$i] =& new xmlrpcresp(0, $code, $str);
1854 break;
1855 default:
1856 return false;
1857 }
1858 }
1859 return $response;
1860 }
1861 else // return type == 'xmlrpcvals'
1862 {
1863 $rets = $result->value();
1864 if($rets->kindOf() != 'array')
1865 {
1866 return false; // bad return type from system.multicall
1867 }
1868 $numRets = $rets->arraysize();
1869 if($numRets != count($msgs))
1870 {
1871 return false; // wrong number of return values.
1872 }
1873
1874 $response = array();
1875 for($i = 0; $i < $numRets; $i++)
1876 {
1877 $val = $rets->arraymem($i);
1878 switch($val->kindOf())
1879 {
1880 case 'array':
1881 if($val->arraysize() != 1)
1882 {
1883 return false; // Bad value
1884 }
1885 // Normal return value
1886 $response[$i] =& new xmlrpcresp($val->arraymem(0));
1887 break;
1888 case 'struct':
1889 $code = $val->structmem('faultCode');
1890 if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1891 {
1892 return false;
1893 }
1894 $str = $val->structmem('faultString');
1895 if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1896 {
1897 return false;
1898 }
1899 $response[$i] =& new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1900 break;
1901 default:
1902 return false;
1903 }
1904 }
1905 return $response;
1906 }
1907 }
1908 } // end class xmlrpc_client
1909
1910 class xmlrpcresp
1911 {
1912 var $val = 0;
1913 var $valtyp;
1914 var $errno = 0;
1915 var $errstr = '';
1916 var $payload;
1917 var $hdrs = array();
1918 var $_cookies = array();
1919 var $content_type = 'text/xml';
1920 var $raw_data = '';
1921
1922 /**
1923 * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1924 * @param integer $fcode set it to anything but 0 to create an error response
1925 * @param string $fstr the error string, in case of an error response
1926 * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1927 *
1928 * @todo add check that $val / $fcode / $fstr is of correct type???
1929 * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1930 * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1931 */
1932 function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1933 {
1934 if($fcode != 0)
1935 {
1936 // error response
1937 $this->errno = $fcode;
1938 $this->errstr = $fstr;
1939 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1940 }
1941 else
1942 {
1943 // successful response
1944 $this->val = $val;
1945 if ($valtyp == '')
1946 {
1947 // user did not declare type of response value: try to guess it
1948 if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1949 {
1950 $this->valtyp = 'xmlrpcvals';
1951 }
1952 else if (is_string($this->val))
1953 {
1954 $this->valtyp = 'xml';
1955
1956 }
1957 else
1958 {
1959 $this->valtyp = 'phpvals';
1960 }
1961 }
1962 else
1963 {
1964 // user declares type of resp value: believe him
1965 $this->valtyp = $valtyp;
1966 }
1967 }
1968 }
1969
1970 /**
1971 * Returns the error code of the response.
1972 * @return integer the error code of this response (0 for not-error responses)
1973 * @access public
1974 */
1975 function faultCode()
1976 {
1977 return $this->errno;
1978 }
1979
1980 /**
1981 * Returns the error code of the response.
1982 * @return string the error string of this response ('' for not-error responses)
1983 * @access public
1984 */
1985 function faultString()
1986 {
1987 return $this->errstr;
1988 }
1989
1990 /**
1991 * Returns the value received by the server.
1992 * @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
1993 * @access public
1994 */
1995 function value()
1996 {
1997 return $this->val;
1998 }
1999
2000 /**
2001 * Returns an array with the cookies received from the server.
2002 * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2003 * with attributes being e.g. 'expires', 'path', domain'.
2004 * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2005 * are still present in the array. It is up to the user-defined code to decide
2006 * how to use the received cookies, and wheter they have to be sent back with the next
2007 * request to the server (using xmlrpc_client::setCookie) or not
2008 * @return array array of cookies received from the server
2009 * @access public
2010 */
2011 function cookies()
2012 {
2013 return $this->_cookies;
2014 }
2015
2016 /**
2017 * Returns xml representation of the response. XML prologue not included
2018 * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2019 * @return string the xml representation of the response
2020 * @access public
2021 */
2022 function serialize($charset_encoding='')
2023 {
2024 if ($charset_encoding != '')
2025 $this->content_type = 'text/xml; charset=' . $charset_encoding;
2026 else
2027 $this->content_type = 'text/xml';
2028 $result = "<methodResponse>\n";
2029 if($this->errno)
2030 {
2031 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2032 // by xml-encoding non ascii chars
2033 $result .= "<fault>\n" .
2034"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2035"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2036xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2037"</struct>\n</value>\n</fault>";
2038 }
2039 else
2040 {
2041 if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2042 {
2043 if (is_string($this->val) && $this->valtyp == 'xml')
2044 {
2045 $result .= "<params>\n<param>\n" .
2046 $this->val .
2047 "</param>\n</params>";
2048 }
2049 else
2050 {
2051 /// @todo try to build something serializable?
2052 die('cannot serialize xmlrpcresp objects whose content is native php values');
2053 }
2054 }
2055 else
2056 {
2057 $result .= "<params>\n<param>\n" .
2058 $this->val->serialize($charset_encoding) .
2059 "</param>\n</params>";
2060 }
2061 }
2062 $result .= "\n</methodResponse>";
2063 $this->payload = $result;
2064 return $result;
2065 }
2066 }
2067
2068 class xmlrpcmsg
2069 {
2070 var $payload;
2071 var $methodname;
2072 var $params=array();
2073 var $debug=0;
2074 var $content_type = 'text/xml';
2075
2076 /**
2077 * @param string $meth the name of the method to invoke
2078 * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2079 */
2080 function xmlrpcmsg($meth, $pars=0)
2081 {
2082 $this->methodname=$meth;
2083 if(is_array($pars) && count($pars)>0)
2084 {
2085 for($i=0; $i<count($pars); $i++)
2086 {
2087 $this->addParam($pars[$i]);
2088 }
2089 }
2090 }
2091
2092 /**
2093 * @access private
2094 */
2095 function xml_header($charset_encoding='')
2096 {
2097 if ($charset_encoding != '')
2098 {
2099 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2100 }
2101 else
2102 {
2103 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2104 }
2105 }
2106
2107 /**
2108 * @access private
2109 */
2110 function xml_footer()
2111 {
2112 return '</methodCall>';
2113 }
2114
2115 /**
2116 * @access private
2117 */
2118 function kindOf()
2119 {
2120 return 'msg';
2121 }
2122
2123 /**
2124 * @access private
2125 */
2126 function createPayload($charset_encoding='')
2127 {
2128 if ($charset_encoding != '')
2129 $this->content_type = 'text/xml; charset=' . $charset_encoding;
2130 else
2131 $this->content_type = 'text/xml';
2132 $this->payload=$this->xml_header($charset_encoding);
2133 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2134 $this->payload.="<params>\n";
2135 for($i=0; $i<count($this->params); $i++)
2136 {
2137 $p=$this->params[$i];
2138 $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2139 "</param>\n";
2140 }
2141 $this->payload.="</params>\n";
2142 $this->payload.=$this->xml_footer();
2143 }
2144
2145 /**
2146 * Gets/sets the xmlrpc method to be invoked
2147 * @param string $meth the method to be set (leave empty not to set it)
2148 * @return string the method that will be invoked
2149 * @access public
2150 */
2151 function method($meth='')
2152 {
2153 if($meth!='')
2154 {
2155 $this->methodname=$meth;
2156 }
2157 return $this->methodname;
2158 }
2159
2160 /**
2161 * Returns xml representation of the message. XML prologue included
2162 * @return string the xml representation of the message, xml prologue included
2163 * @access public
2164 */
2165 function serialize($charset_encoding='')
2166 {
2167 $this->createPayload($charset_encoding);
2168 return $this->payload;
2169 }
2170
2171 /**
2172 * Add a parameter to the list of parameters to be used upon method invocation
2173 * @param xmlrpcval $par
2174 * @return boolean false on failure
2175 * @access public
2176 */
2177 function addParam($par)
2178 {
2179 // add check: do not add to self params which are not xmlrpcvals
2180 if(is_object($par) && is_a($par, 'xmlrpcval'))
2181 {
2182 $this->params[]=$par;
2183 return true;
2184 }
2185 else
2186 {
2187 return false;
2188 }
2189 }
2190
2191 /**
2192 * Returns the nth parameter in the message. The index zero-based.
2193 * @param integer $i the index of the parameter to fetch (zero based)
2194 * @return xmlrpcval the i-th parameter
2195 * @access public
2196 */
2197 function getParam($i) { return $this->params[$i]; }
2198
2199 /**
2200 * Returns the number of parameters in the messge.
2201 * @return integer the number of parameters currently set
2202 * @access public
2203 */
2204 function getNumParams() { return count($this->params); }
2205
2206 /**
2207 * Given an open file handle, read all data available and parse it as axmlrpc response.
2208 * NB: the file handle is not closed by this function.
2209 * @access public
2210 * @return xmlrpcresp
2211 * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2212 */
2213 function &parseResponseFile($fp)
2214 {
2215 $ipd='';
2216 while($data=fread($fp, 32768))
2217 {
2218 $ipd.=$data;
2219 }
2220 //fclose($fp);
2221 $r =& $this->parseResponse($ipd);
2222 return $r;
2223 }
2224
2225 /**
2226 * Parses HTTP headers and separates them from data.
2227 * @access private
2228 */
2229 function &parseResponseHeaders(&$data, $headers_processed=false)
2230 {
2231 // Support "web-proxy-tunelling" connections for https through proxies
2232 if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2233 {
2234 // Look for CR/LF or simple LF as line separator,
2235 // (even though it is not valid http)
2236 $pos = strpos($data,"\r\n\r\n");
2237 if($pos || is_int($pos))
2238 {
2239 $bd = $pos+4;
2240 }
2241 else
2242 {
2243 $pos = strpos($data,"\n\n");
2244 if($pos || is_int($pos))
2245 {
2246 $bd = $pos+2;
2247 }
2248 else
2249 {
2250 // No separation between response headers and body: fault?
2251 $bd = 0;
2252 }
2253 }
2254 if ($bd)
2255 {
2256 // this filters out all http headers from proxy.
2257 // maybe we could take them into account, too?
2258 $data = substr($data, $bd);
2259 }
2260 else
2261 {
2262 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTPS via proxy error, tunnel connection possibly failed');
2263 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2264 return $r;
2265 }
2266 }
2267
2268 // Strip HTTP 1.1 100 Continue header if present
2269 while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2270 {
2271 $pos = strpos($data, 'HTTP', 12);
2272 // server sent a Continue header without any (valid) content following...
2273 // give the client a chance to know it
2274 if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2275 {
2276 break;
2277 }
2278 $data = substr($data, $pos);
2279 }
2280 if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2281 {
2282 $errstr= substr($data, 0, strpos($data, "\n")-1);
2283 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
2284 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2285 return $r;
2286 }
2287
2288 $GLOBALS['_xh']['headers'] = array();
2289 $GLOBALS['_xh']['cookies'] = array();
2290
2291 // be tolerant to usage of \n instead of \r\n to separate headers and data
2292 // (even though it is not valid http)
2293 $pos = strpos($data,"\r\n\r\n");
2294 if($pos || is_int($pos))
2295 {
2296 $bd = $pos+4;
2297 }
2298 else
2299 {
2300 $pos = strpos($data,"\n\n");
2301 if($pos || is_int($pos))
2302 {
2303 $bd = $pos+2;
2304 }
2305 else
2306 {
2307 // No separation between response headers and body: fault?
2308 // we could take some action here instead of going on...
2309 $bd = 0;
2310 }
2311 }
2312 // be tolerant to line endings, and extra empty lines
2313 $ar = split("\r?\n", trim(substr($data, 0, $pos)));
2314 while(list(,$line) = @each($ar))
2315 {
2316 // take care of multi-line headers and cookies
2317 $arr = explode(':',$line,2);
2318 if(count($arr) > 1)
2319 {
2320 $header_name = strtolower(trim($arr[0]));
2321 /// @todo some other headers (the ones that allow a CSV list of values)
2322 /// do allow many values to be passed using multiple header lines.
2323 /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2324 /// instead of replacing it for those...
2325 if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2326 {
2327 if ($header_name == 'set-cookie2')
2328 {
2329 // version 2 cookies:
2330 // there could be many cookies on one line, comma separated
2331 $cookies = explode(',', $arr[1]);
2332 }
2333 else
2334 {
2335 $cookies = array($arr[1]);
2336 }
2337 foreach ($cookies as $cookie)
2338 {
2339 // glue together all received cookies, using a comma to separate them
2340 // (same as php does with getallheaders())
2341 if (isset($GLOBALS['_xh']['headers'][$header_name]))
2342 $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2343 else
2344 $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2345 // parse cookie attributes, in case user wants to correctly honour them
2346 // feature creep: only allow rfc-compliant cookie attributes?
2347 // @todo support for server sending multiple time cookie with same name, but using different PATHs
2348 $cookie = explode(';', $cookie);
2349 foreach ($cookie as $pos => $val)
2350 {
2351 $val = explode('=', $val, 2);
2352 $tag = trim($val[0]);
2353 $val = trim(@$val[1]);
2354 /// @todo with version 1 cookies, we should strip leading and trailing " chars
2355 if ($pos == 0)
2356 {
2357 $cookiename = $tag;
2358 $GLOBALS['_xh']['cookies'][$tag] = array();
2359 $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2360 }
2361 else
2362 {
2363 if ($tag != 'value')
2364 {
2365 $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2366 }
2367 }
2368 }
2369 }
2370 }
2371 else
2372 {
2373 $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2374 }
2375 }
2376 elseif(isset($header_name))
2377 {
2378 /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
2379 $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2380 }
2381 }
2382
2383 $data = substr($data, $bd);
2384
2385 if($this->debug && count($GLOBALS['_xh']['headers']))
2386 {
2387 print '<PRE>';
2388 foreach($GLOBALS['_xh']['headers'] as $header => $value)
2389 {
2390 print htmlentities("HEADER: $header: $value\n");
2391 }
2392 foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2393 {
2394 print htmlentities("COOKIE: $header={$value['value']}\n");
2395 }
2396 print "</PRE>\n";
2397 }
2398
2399 // if CURL was used for the call, http headers have been processed,
2400 // and dechunking + reinflating have been carried out
2401 if(!$headers_processed)
2402 {
2403 // Decode chunked encoding sent by http 1.1 servers
2404 if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2405 {
2406 if(!$data = decode_chunked($data))
2407 {
2408 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
2409 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2410 return $r;
2411 }
2412 }
2413
2414 // Decode gzip-compressed stuff
2415 // code shamelessly inspired from nusoap library by Dietrich Ayala
2416 if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2417 {
2418 $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2419 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2420 {
2421 // if decoding works, use it. else assume data wasn't gzencoded
2422 if(function_exists('gzinflate'))
2423 {
2424 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2425 {
2426 $data = $degzdata;
2427 if($this->debug)
2428 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2429 }
2430 elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2431 {
2432 $data = $degzdata;
2433 if($this->debug)
2434 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2435 }
2436 else
2437 {
2438 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
2439 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2440 return $r;
2441 }
2442 }
2443 else
2444 {
2445 error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2446 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2447 return $r;
2448 }
2449 }
2450 }
2451 } // end of 'if needed, de-chunk, re-inflate response'
2452
2453 // real stupid hack to avoid PHP 4 complaining about returning NULL by ref
2454 $r = null;
2455 $r =& $r;
2456 return $r;
2457 }
2458
2459 /**
2460 * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2461 * @param string $data the xmlrpc response, eventually including http headers
2462 * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2463 * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2464 * @return xmlrpcresp
2465 * @access public
2466 */
2467 function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2468 {
2469 if($this->debug)
2470 {
2471 //by maHo, replaced htmlspecialchars with htmlentities
2472 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2473 }
2474
2475 if($data == '')
2476 {
2477 error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.');
2478 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2479 return $r;
2480 }
2481
2482 $GLOBALS['_xh']=array();
2483
2484 $raw_data = $data;
2485 // parse the HTTP headers of the response, if present, and separate them from data
2486 if(substr($data, 0, 4) == 'HTTP')
2487 {
2488 $r =& $this->parseResponseHeaders($data, $headers_processed);
2489 if ($r)
2490 {
2491 // failed processing of HTTP response headers
2492 // save into response obj the full payload received, for debugging
2493 $r->raw_data = $data;
2494 return $r;
2495 }
2496 }
2497 else
2498 {
2499 $GLOBALS['_xh']['headers'] = array();
2500 $GLOBALS['_xh']['cookies'] = array();
2501 }
2502
2503 if($this->debug)
2504 {
2505 $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2506 if ($start)
2507 {
2508 $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2509 $end = strpos($data, '-->', $start);
2510 $comments = substr($data, $start, $end-$start);
2511 print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2512 }
2513 }
2514
2515 // be tolerant of extra whitespace in response body
2516 $data = trim($data);
2517
2518 /// @todo return an error msg if $data=='' ?
2519
2520 // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2521 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
2522 $bd = false;
2523 // Poor man's version of strrpos for php 4...
2524 $pos = strpos($data, '</methodResponse>');
2525 while($pos || is_int($pos))
2526 {
2527 $bd = $pos+17;
2528 $pos = strpos($data, '</methodResponse>', $bd);
2529 }
2530 if($bd)
2531 {
2532 $data = substr($data, 0, $bd);
2533 }
2534
2535 // if user wants back raw xml, give it to him
2536 if ($return_type == 'xml')
2537 {
2538 $r =& new xmlrpcresp($data, 0, '', 'xml');
2539 $r->hdrs = $GLOBALS['_xh']['headers'];
2540 $r->_cookies = $GLOBALS['_xh']['cookies'];
2541 $r->raw_data = $raw_data;
2542 return $r;
2543 }
2544
2545 // try to 'guestimate' the character encoding of the received response
2546 $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2547
2548 $GLOBALS['_xh']['ac']='';
2549 //$GLOBALS['_xh']['qt']=''; //unused...
2550 $GLOBALS['_xh']['stack'] = array();
2551 $GLOBALS['_xh']['valuestack'] = array();
2552 $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2553 $GLOBALS['_xh']['isf_reason']='';
2554 $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2555
2556 // if response charset encoding is not known / supported, try to use
2557 // the default encoding and parse the xml anyway, but log a warning...
2558 if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2559 // the following code might be better for mb_string enabled installs, but
2560 // makes the lib about 200% slower...
2561 //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2562 {
2563 error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding);
2564 $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2565 }
2566 $parser = xml_parser_create($resp_encoding);
2567 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2568 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2569 // the xml parser to give us back data in the expected charset.
2570 // What if internal encoding is not in one of the 3 allowed?
2571 // we use the broadest one, ie. utf8
2572 // This allows to send data which is native in various charset,
2573 // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2574 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2575 {
2576 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2577 }
2578 else
2579 {
2580 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2581 }
2582
2583 if ($return_type == 'phpvals')
2584 {
2585 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2586 }
2587 else
2588 {
2589 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2590 }
2591
2592 xml_set_character_data_handler($parser, 'xmlrpc_cd');
2593 xml_set_default_handler($parser, 'xmlrpc_dh');
2594
2595 // first error check: xml not well formed
2596 if(!xml_parse($parser, $data, count($data)))
2597 {
2598 // thanks to Peter Kocks <peter.kocks@baygate.com>
2599 if((xml_get_current_line_number($parser)) == 1)
2600 {
2601 $errstr = 'XML error at line 1, check URL';
2602 }
2603 else
2604 {
2605 $errstr = sprintf('XML error: %s at line %d, column %d',
2606 xml_error_string(xml_get_error_code($parser)),
2607 xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2608 }
2609 error_log($errstr);
2610 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2611 xml_parser_free($parser);
2612 if($this->debug)
2613 {
2614 print $errstr;
2615 }
2616 $r->hdrs = $GLOBALS['_xh']['headers'];
2617 $r->_cookies = $GLOBALS['_xh']['cookies'];
2618 $r->raw_data = $raw_data;
2619 return $r;
2620 }
2621 xml_parser_free($parser);
2622 // second error check: xml well formed but not xml-rpc compliant
2623 if ($GLOBALS['_xh']['isf'] > 1)
2624 {
2625 if ($this->debug)
2626 {
2627 /// @todo echo something for user?
2628 }
2629
2630 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2631 $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2632 }
2633 // third error check: parsing of the response has somehow gone boink.
2634 // NB: shall we omit this check, since we trust the parsing code?
2635 elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2636 {
2637 // something odd has happened
2638 // and it's time to generate a client side error
2639 // indicating something odd went on
2640 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2641 $GLOBALS['xmlrpcstr']['invalid_return']);
2642 }
2643 else
2644 {
2645 if ($this->debug)
2646 {
2647 print "<PRE>---PARSED---\n";
2648 // somehow htmlentities chokes on var_export, and some full html string...
2649 //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2650 print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2651 print "\n---END---</PRE>";
2652 }
2653
2654 // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2655 $v =& $GLOBALS['_xh']['value'];
2656
2657 if($GLOBALS['_xh']['isf'])
2658 {
2659 /// @todo we should test here if server sent an int and a string,
2660 /// and/or coerce them into such...
2661 if ($return_type == 'xmlrpcvals')
2662 {
2663 $errno_v = $v->structmem('faultCode');
2664 $errstr_v = $v->structmem('faultString');
2665 $errno = $errno_v->scalarval();
2666 $errstr = $errstr_v->scalarval();
2667 }
2668 else
2669 {
2670 $errno = $v['faultCode'];
2671 $errstr = $v['faultString'];
2672 }
2673
2674 if($errno == 0)
2675 {
2676 // FAULT returned, errno needs to reflect that
2677 $errno = -1;
2678 }
2679
2680 $r =& new xmlrpcresp(0, $errno, $errstr);
2681 }
2682 else
2683 {
2684 $r=&new xmlrpcresp($v, 0, '', $return_type);
2685 }
2686 }
2687
2688 $r->hdrs = $GLOBALS['_xh']['headers'];
2689 $r->_cookies = $GLOBALS['_xh']['cookies'];
2690 $r->raw_data = $raw_data;
2691 return $r;
2692 }
2693 }
2694
2695 class xmlrpcval
2696 {
2697 var $me=array();
2698 var $mytype=0;
2699 var $_php_class=null;
2700
2701 /**
2702 * @param mixed $val
2703 * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2704 */
2705 function xmlrpcval($val=-1, $type='')
2706 {
2707 /// @todo: optimization creep - do not call addXX, do it all inline.
2708 /// downside: booleans will not be coerced anymore
2709 if($val!==-1 || $type!='')
2710 {
2711 // optimization creep: inlined all work done by constructor
2712 switch($type)
2713 {
2714 case '':
2715 $this->mytype=1;
2716 $this->me['string']=$val;
2717 break;
2718 case 'i4':
2719 case 'int':
2720 case 'double':
2721 case 'string':
2722 case 'boolean':
2723 case 'dateTime.iso8601':
2724 case 'base64':
2725 case 'null':
2726 $this->mytype=1;
2727 $this->me[$type]=$val;
2728 break;
2729 case 'array':
2730 $this->mytype=2;
2731 $this->me['array']=$val;
2732 break;
2733 case 'struct':
2734 $this->mytype=3;
2735 $this->me['struct']=$val;
2736 break;
2737 default:
2738 error_log("XML-RPC: xmlrpcval::xmlrpcval: not a known type ($type)");
2739 }
2740 /*if($type=='')
2741 {
2742 $type='string';
2743 }
2744 if($GLOBALS['xmlrpcTypes'][$type]==1)
2745 {
2746 $this->addScalar($val,$type);
2747 }
2748 elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2749 {
2750 $this->addArray($val);
2751 }
2752 elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2753 {
2754 $this->addStruct($val);
2755 }*/
2756 }
2757 }
2758
2759 /**
2760 * Add a single php value to an (unitialized) xmlrpcval
2761 * @param mixed $val
2762 * @param string $type
2763 * @return int 1 or 0 on failure
2764 */
2765 function addScalar($val, $type='string')
2766 {
2767 $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2768 if($typeof!=1)
2769 {
2770 error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($type)");
2771 return 0;
2772 }
2773
2774 // coerce booleans into correct values
2775 // NB: we should iether do it for datetimes, integers and doubles, too,
2776 // or just plain remove this check, implemnted on booleans only...
2777 if($type==$GLOBALS['xmlrpcBoolean'])
2778 {
2779 if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2780 {
2781 $val=true;
2782 }
2783 else
2784 {
2785 $val=false;
2786 }
2787 }
2788
2789 switch($this->mytype)
2790 {
2791 case 1:
2792 error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value');
2793 return 0;
2794 case 3:
2795 error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval');
2796 return 0;
2797 case 2:
2798 // we're adding a scalar value to an array here
2799 //$ar=$this->me['array'];
2800 //$ar[]=&new xmlrpcval($val, $type);
2801 //$this->me['array']=$ar;
2802 // Faster (?) avoid all the costly array-copy-by-val done here...
2803 $this->me['array'][]=&new xmlrpcval($val, $type);
2804 return 1;
2805 default:
2806 // a scalar, so set the value and remember we're scalar
2807 $this->me[$type]=$val;
2808 $this->mytype=$typeof;
2809 return 1;
2810 }
2811 }
2812
2813 /**
2814 * Add an array of xmlrpcval objects to an xmlrpcval
2815 * @param array $vals
2816 * @return int 1 or 0 on failure
2817 * @access public
2818 *
2819 * @todo add some checking for $vals to be an array of xmlrpcvals?
2820 */
2821 function addArray($vals)
2822 {
2823 if($this->mytype==0)
2824 {
2825 $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2826 $this->me['array']=$vals;
2827 return 1;
2828 }
2829 elseif($this->mytype==2)
2830 {
2831 // we're adding to an array here
2832 $this->me['array'] = array_merge($this->me['array'], $vals);
2833 return 1;
2834 }
2835 else
2836 {
2837 error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']');
2838 return 0;
2839 }
2840 }
2841
2842 /**
2843 * Add an array of named xmlrpcval objects to an xmlrpcval
2844 * @param array $vals
2845 * @return int 1 or 0 on failure
2846 * @access public
2847 *
2848 * @todo add some checking for $vals to be an array?
2849 */
2850 function addStruct($vals)
2851 {
2852 if($this->mytype==0)
2853 {
2854 $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2855 $this->me['struct']=$vals;
2856 return 1;
2857 }
2858 elseif($this->mytype==3)
2859 {
2860 // we're adding to a struct here
2861 $this->me['struct'] = array_merge($this->me['struct'], $vals);
2862 return 1;
2863 }
2864 else
2865 {
2866 error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']');
2867 return 0;
2868 }
2869 }
2870
2871 // poor man's version of print_r ???
2872 // DEPRECATED!
2873 function dump($ar)
2874 {
2875 foreach($ar as $key => $val)
2876 {
2877 echo "$key => $val<br />";
2878 if($key == 'array')
2879 {
2880 while(list($key2, $val2) = each($val))
2881 {
2882 echo "-- $key2 => $val2<br />";
2883 }
2884 }
2885 }
2886 }
2887
2888 /**
2889 * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2890 * @return string
2891 * @access public
2892 */
2893 function kindOf()
2894 {
2895 switch($this->mytype)
2896 {
2897 case 3:
2898 return 'struct';
2899 break;
2900 case 2:
2901 return 'array';
2902 break;
2903 case 1:
2904 return 'scalar';
2905 break;
2906 default:
2907 return 'undef';
2908 }
2909 }
2910
2911 /**
2912 * @access private
2913 */
2914 function serializedata($typ, $val, $charset_encoding='')
2915 {
2916 $rs='';
2917 switch(@$GLOBALS['xmlrpcTypes'][$typ])
2918 {
2919 case 1:
2920 switch($typ)
2921 {
2922 case $GLOBALS['xmlrpcBase64']:
2923 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2924 break;
2925 case $GLOBALS['xmlrpcBoolean']:
2926 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2927 break;
2928 case $GLOBALS['xmlrpcString']:
2929 // G. Giunta 2005/2/13: do NOT use htmlentities, since
2930 // it will produce named html entities, which are invalid xml
2931 $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2932 break;
2933 case $GLOBALS['xmlrpcInt']:
2934 case $GLOBALS['xmlrpcI4']:
2935 $rs.="<${typ}>".(int)$val."</${typ}>";
2936 break;
2937 case $GLOBALS['xmlrpcDouble']:
2938 $rs.="<${typ}>".(double)$val."</${typ}>";
2939 break;
2940 case $GLOBALS['xmlrpcNull']:
2941 $rs.="<nil/>";
2942 break;
2943 default:
2944 // no standard type value should arrive here, but provide a possibility
2945 // for xmlrpcvals of unknown type...
2946 $rs.="<${typ}>${val}</${typ}>";
2947 }
2948 break;
2949 case 3:
2950 // struct
2951 if ($this->_php_class)
2952 {
2953 $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2954 }
2955 else
2956 {
2957 $rs.="<struct>\n";
2958 }
2959 foreach($val as $key2 => $val2)
2960 {
2961 $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
2962 //$rs.=$this->serializeval($val2);
2963 $rs.=$val2->serialize($charset_encoding);
2964 $rs.="</member>\n";
2965 }
2966 $rs.='</struct>';
2967 break;
2968 case 2:
2969 // array
2970 $rs.="<array>\n<data>\n";
2971 for($i=0; $i<count($val); $i++)
2972 {
2973 //$rs.=$this->serializeval($val[$i]);
2974 $rs.=$val[$i]->serialize($charset_encoding);
2975 }
2976 $rs.="</data>\n</array>";
2977 break;
2978 default:
2979 break;
2980 }
2981 return $rs;
2982 }
2983
2984 /**
2985 * Returns xml representation of the value. XML prologue not included
2986 * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2987 * @return string
2988 * @access public
2989 */
2990 function serialize($charset_encoding='')
2991 {
2992 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
2993 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
2994 //{
2995 reset($this->me);
2996 list($typ, $val) = each($this->me);
2997 return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
2998 //}
2999 }
3000
3001 // DEPRECATED
3002 function serializeval($o)
3003 {
3004 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3005 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3006 //{
3007 $ar=$o->me;
3008 reset($ar);
3009 list($typ, $val) = each($ar);
3010 return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3011 //}
3012 }
3013
3014 /**
3015 * Checks wheter a struct member with a given name is present.
3016 * Works only on xmlrpcvals of type struct.
3017 * @param string $m the name of the struct member to be looked up
3018 * @return boolean
3019 * @access public
3020 */
3021 function structmemexists($m)
3022 {
3023 return array_key_exists($m, $this->me['struct']);
3024 }
3025
3026 /**
3027 * Returns the value of a given struct member (an xmlrpcval object in itself).
3028 * Will raise a php warning if struct member of given name does not exist
3029 * @param string $m the name of the struct member to be looked up
3030 * @return xmlrpcval
3031 * @access public
3032 */
3033 function structmem($m)
3034 {
3035 return $this->me['struct'][$m];
3036 }
3037
3038 /**
3039 * Reset internal pointer for xmlrpcvals of type struct.
3040 * @access public
3041 */
3042 function structreset()
3043 {
3044 reset($this->me['struct']);
3045 }
3046
3047 /**
3048 * Return next member element for xmlrpcvals of type struct.
3049 * @return xmlrpcval
3050 * @access public
3051 */
3052 function structeach()
3053 {
3054 return each($this->me['struct']);
3055 }
3056
3057 // DEPRECATED! this code looks like it is very fragile and has not been fixed
3058 // for a long long time. Shall we remove it for 2.0?
3059 function getval()
3060 {
3061 // UNSTABLE
3062 reset($this->me);
3063 list($a,$b)=each($this->me);
3064 // contributed by I Sofer, 2001-03-24
3065 // add support for nested arrays to scalarval
3066 // i've created a new method here, so as to
3067 // preserve back compatibility
3068
3069 if(is_array($b))
3070 {
3071 @reset($b);
3072 while(list($id,$cont) = @each($b))
3073 {
3074 $b[$id] = $cont->scalarval();
3075 }
3076 }
3077
3078 // add support for structures directly encoding php objects
3079 if(is_object($b))
3080 {
3081 $t = get_object_vars($b);
3082 @reset($t);
3083 while(list($id,$cont) = @each($t))
3084 {
3085 $t[$id] = $cont->scalarval();
3086 }
3087 @reset($t);
3088 while(list($id,$cont) = @each($t))
3089 {
3090 @$b->$id = $cont;
3091 }
3092 }
3093 // end contrib
3094 return $b;
3095 }
3096
3097 /**
3098 * Returns the value of a scalar xmlrpcval
3099 * @return mixed
3100 * @access public
3101 */
3102 function scalarval()
3103 {
3104 reset($this->me);
3105 list(,$b)=each($this->me);
3106 return $b;
3107 }
3108
3109 /**
3110 * Returns the type of the xmlrpcval.
3111 * For integers, 'int' is always returned in place of 'i4'
3112 * @return string
3113 * @access public
3114 */
3115 function scalartyp()
3116 {
3117 reset($this->me);
3118 list($a,)=each($this->me);
3119 if($a==$GLOBALS['xmlrpcI4'])
3120 {
3121 $a=$GLOBALS['xmlrpcInt'];
3122 }
3123 return $a;
3124 }
3125
3126 /**
3127 * Returns the m-th member of an xmlrpcval of struct type
3128 * @param integer $m the index of the value to be retrieved (zero based)
3129 * @return xmlrpcval
3130 * @access public
3131 */
3132 function arraymem($m)
3133 {
3134 return $this->me['array'][$m];
3135 }
3136
3137 /**
3138 * Returns the number of members in an xmlrpcval of array type
3139 * @return integer
3140 * @access public
3141 */
3142 function arraysize()
3143 {
3144 return count($this->me['array']);
3145 }
3146
3147 /**
3148 * Returns the number of members in an xmlrpcval of struct type
3149 * @return integer
3150 * @access public
3151 */
3152 function structsize()
3153 {
3154 return count($this->me['struct']);
3155 }
3156 }
3157
3158
3159 // date helpers
3160
3161 /**
3162 * Given a timestamp, return the corresponding ISO8601 encoded string.
3163 *
3164 * Really, timezones ought to be supported
3165 * but the XML-RPC spec says:
3166 *
3167 * "Don't assume a timezone. It should be specified by the server in its
3168 * documentation what assumptions it makes about timezones."
3169 *
3170 * These routines always assume localtime unless
3171 * $utc is set to 1, in which case UTC is assumed
3172 * and an adjustment for locale is made when encoding
3173 *
3174 * @param int $timet (timestamp)
3175 * @param int $utc (0 or 1)
3176 * @return string
3177 */
3178 function iso8601_encode($timet, $utc=0)
3179 {
3180 if(!$utc)
3181 {
3182 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3183 }
3184 else
3185 {
3186 if(function_exists('gmstrftime'))
3187 {
3188 // gmstrftime doesn't exist in some versions
3189 // of PHP
3190 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3191 }
3192 else
3193 {
3194 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3195 }
3196 }
3197 return $t;
3198 }
3199
3200 /**
3201 * Given an ISO8601 date string, return a timet in the localtime, or UTC
3202 * @param string $idate
3203 * @param int $utc either 0 or 1
3204 * @return int (datetime)
3205 */
3206 function iso8601_decode($idate, $utc=0)
3207 {
3208 $t=0;
3209 if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3210 {
3211 if($utc)
3212 {
3213 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3214 }
3215 else
3216 {
3217 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3218 }
3219 }
3220 return $t;
3221 }
3222
3223 /**
3224 * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3225 *
3226 * Works with xmlrpc message objects as input, too.
3227 *
3228 * Given proper options parameter, can rebuild generic php object instances
3229 * (provided those have been encoded to xmlrpc format using a corresponding
3230 * option in php_xmlrpc_encode())
3231 * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3232 * This means that the remote communication end can decide which php code will
3233 * get executed on your server, leaving the door possibly open to 'php-injection'
3234 * style of attacks (provided you have some classes defined on your server that
3235 * might wreak havoc if instances are built outside an appropriate context).
3236 * Make sure you trust the remote server/client before eanbling this!
3237 *
3238 * @author Dan Libby (dan@libby.com)
3239 *
3240 * @param xmlrpcval $xmlrpc_val
3241 * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects
3242 * @return mixed
3243 */
3244 function php_xmlrpc_decode($xmlrpc_val, $options=array())
3245 {
3246 switch($xmlrpc_val->kindOf())
3247 {
3248 case 'scalar':
3249 if (in_array('extension_api', $options))
3250 {
3251 reset($xmlrpc_val->me);
3252 list($typ,$val) = each($xmlrpc_val->me);
3253 switch ($typ)
3254 {
3255 case 'dateTime.iso8601':
3256 $xmlrpc_val->scalar = $val;
3257 $xmlrpc_val->xmlrpc_type = 'datetime';
3258 $xmlrpc_val->timestamp = iso8601_decode($val);
3259 return $xmlrpc_val;
3260 case 'base64':
3261 $xmlrpc_val->scalar = $val;
3262 $xmlrpc_val->type = $typ;
3263 return $xmlrpc_val;
3264 default:
3265 return $xmlrpc_val->scalarval();
3266 }
3267 }
3268 return $xmlrpc_val->scalarval();
3269 case 'array':
3270 $size = $xmlrpc_val->arraysize();
3271 $arr = array();
3272 for($i = 0; $i < $size; $i++)
3273 {
3274 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3275 }
3276 return $arr;
3277 case 'struct':
3278 $xmlrpc_val->structreset();
3279 // If user said so, try to rebuild php objects for specific struct vals.
3280 /// @todo should we raise a warning for class not found?
3281 // shall we check for proper subclass of xmlrpcval instead of
3282 // presence of _php_class to detect what we can do?
3283 if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3284 && class_exists($xmlrpc_val->_php_class))
3285 {
3286 $obj = @new $xmlrpc_val->_php_class;
3287 while(list($key,$value)=$xmlrpc_val->structeach())
3288 {
3289 $obj->$key = php_xmlrpc_decode($value, $options);
3290 }
3291 return $obj;
3292 }
3293 else
3294 {
3295 $arr = array();
3296 while(list($key,$value)=$xmlrpc_val->structeach())
3297 {
3298 $arr[$key] = php_xmlrpc_decode($value, $options);
3299 }
3300 return $arr;
3301 }
3302 case 'msg':
3303 $paramcount = $xmlrpc_val->getNumParams();
3304 $arr = array();
3305 for($i = 0; $i < $paramcount; $i++)
3306 {
3307 $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3308 }
3309 return $arr;
3310 }
3311 }
3312
3313 // This constant left here only for historical reasons...
3314 // it was used to decide if we have to define xmlrpc_encode on our own, but
3315 // we do not do it anymore
3316 if(function_exists('xmlrpc_decode'))
3317 {
3318 define('XMLRPC_EPI_ENABLED','1');
3319 }
3320 else
3321 {
3322 define('XMLRPC_EPI_ENABLED','0');
3323 }
3324
3325 /**
3326 * Takes native php types and encodes them into xmlrpc PHP object format.
3327 * It will not re-encode xmlrpcval objects.
3328 *
3329 * Feature creep -- could support more types via optional type argument
3330 * (string => datetime support has been added, ??? => base64 not yet)
3331 *
3332 * If given a proper options parameter, php object instances will be encoded
3333 * into 'special' xmlrpc values, that can later be decoded into php objects
3334 * by calling php_xmlrpc_decode() with a corresponding option
3335 *
3336 * @author Dan Libby (dan@libby.com)
3337 *
3338 * @param mixed $php_val the value to be converted into an xmlrpcval object
3339 * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3340 * @return xmlrpcval
3341 */
3342 function &php_xmlrpc_encode($php_val, $options=array())
3343 {
3344 $type = gettype($php_val);
3345 switch($type)
3346 {
3347 case 'string':
3348 if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3349 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3350 else
3351 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3352 break;
3353 case 'integer':
3354 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3355 break;
3356 case 'double':
3357 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3358 break;
3359 // <G_Giunta_2001-02-29>
3360 // Add support for encoding/decoding of booleans, since they are supported in PHP
3361 case 'boolean':
3362 $xmlrpc_val =& new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3363 break;
3364 // </G_Giunta_2001-02-29>
3365 case 'array':
3366 // PHP arrays can be encoded to either xmlrpc structs or arrays,
3367 // depending on wheter they are hashes or plain 0..n integer indexed
3368 // A shorter one-liner would be
3369 // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3370 // but execution time skyrockets!
3371 $j = 0;
3372 $arr = array();
3373 $ko = false;
3374 foreach($php_val as $key => $val)
3375 {
3376 $arr[$key] =& php_xmlrpc_encode($val, $options);
3377 if(!$ko && $key !== $j)
3378 {
3379 $ko = true;
3380 }
3381 $j++;
3382 }
3383 if($ko)
3384 {
3385 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3386 }
3387 else
3388 {
3389 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3390 }
3391 break;
3392 case 'object':
3393 if(is_a($php_val, 'xmlrpcval'))
3394 {
3395 $xmlrpc_val = $php_val;
3396 }
3397 else
3398 {
3399 $arr = array();
3400 while(list($k,$v) = each($php_val))
3401 {
3402 $arr[$k] = php_xmlrpc_encode($v, $options);
3403 }
3404 $xmlrpc_val =& new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3405 if (in_array('encode_php_objs', $options))
3406 {
3407 // let's save original class name into xmlrpcval:
3408 // might be useful later on...
3409 $xmlrpc_val->_php_class = get_class($php_val);
3410 }
3411 }
3412 break;
3413 case 'NULL':
3414 if (in_array('extension_api', $options))
3415 {
3416 $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcString']);
3417 }
3418 if (in_array('null_extension', $options))
3419 {
3420 $xmlrpc_val =& new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3421 }
3422 else
3423 {
3424 $xmlrpc_val =& new xmlrpcval();
3425 }
3426 break;
3427 case 'resource':
3428 if (in_array('extension_api', $options))
3429 {
3430 $xmlrpc_val =& new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3431 }
3432 else
3433 {
3434 $xmlrpc_val =& new xmlrpcval();
3435 }
3436 // catch "user function", "unknown type"
3437 default:
3438 // giancarlo pinerolo <ping@alt.it>
3439 // it has to return
3440 // an empty object in case, not a boolean.
3441 $xmlrpc_val =& new xmlrpcval();
3442 break;
3443 }
3444 return $xmlrpc_val;
3445 }
3446
3447 /**
3448 * Convert the xml representation of a method response, method request or single
3449 * xmlrpc value into the appropriate object (a.k.a. deserialize)
3450 * @param string $xml_val
3451 * @param array $options
3452 * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3453 */
3454 function php_xmlrpc_decode_xml($xml_val, $options=array())
3455 {
3456 $GLOBALS['_xh'] = array();
3457 $GLOBALS['_xh']['ac'] = '';
3458 $GLOBALS['_xh']['stack'] = array();
3459 $GLOBALS['_xh']['valuestack'] = array();
3460 $GLOBALS['_xh']['params'] = array();
3461 $GLOBALS['_xh']['pt'] = array();
3462 $GLOBALS['_xh']['isf'] = 0;
3463 $GLOBALS['_xh']['isf_reason'] = '';
3464 $GLOBALS['_xh']['method'] = false;
3465 $GLOBALS['_xh']['rt'] = '';
3466 /// @todo 'guestimate' encoding
3467 $parser = xml_parser_create();
3468 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3469 // What if internal encoding is not in one of the 3 allowed?
3470 // we use the broadest one, ie. utf8!
3471 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3472 {
3473 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3474 }
3475 else
3476 {
3477 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3478 }
3479 xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3480 xml_set_character_data_handler($parser, 'xmlrpc_cd');
3481 xml_set_default_handler($parser, 'xmlrpc_dh');
3482 if(!xml_parse($parser, $xml_val, 1))
3483 {
3484 $errstr = sprintf('XML error: %s at line %d, column %d',
3485 xml_error_string(xml_get_error_code($parser)),
3486 xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3487 error_log($errstr);
3488 xml_parser_free($parser);
3489 return false;
3490 }
3491 xml_parser_free($parser);
3492 if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3493 {
3494 error_log($GLOBALS['_xh']['isf_reason']);
3495 return false;
3496 }
3497 switch ($GLOBALS['_xh']['rt'])
3498 {
3499 case 'methodresponse':
3500 $v =& $GLOBALS['_xh']['value'];
3501 if ($GLOBALS['_xh']['isf'] == 1)
3502 {
3503 $vc = $v->structmem('faultCode');
3504 $vs = $v->structmem('faultString');
3505 $r =& new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3506 }
3507 else
3508 {
3509 $r =& new xmlrpcresp($v);
3510 }
3511 return $r;
3512 case 'methodcall':
3513 $m =& new xmlrpcmsg($GLOBALS['_xh']['method']);
3514 for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3515 {
3516 $m->addParam($GLOBALS['_xh']['params'][$i]);
3517 }
3518 return $m;
3519 case 'value':
3520 return $GLOBALS['_xh']['value'];
3521 default:
3522 return false;
3523 }
3524 }
3525
3526 /**
3527 * decode a string that is encoded w/ "chunked" transfer encoding
3528 * as defined in rfc2068 par. 19.4.6
3529 * code shamelessly stolen from nusoap library by Dietrich Ayala
3530 *
3531 * @param string $buffer the string to be decoded
3532 * @return string
3533 */
3534 function decode_chunked($buffer)
3535 {
3536 // length := 0
3537 $length = 0;
3538 $new = '';
3539
3540 // read chunk-size, chunk-extension (if any) and crlf
3541 // get the position of the linebreak
3542 $chunkend = strpos($buffer,"\r\n") + 2;
3543 $temp = substr($buffer,0,$chunkend);
3544 $chunk_size = hexdec( trim($temp) );
3545 $chunkstart = $chunkend;
3546 while($chunk_size > 0)
3547 {
3548 $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3549
3550 // just in case we got a broken connection
3551 if($chunkend == false)
3552 {
3553 $chunk = substr($buffer,$chunkstart);
3554 // append chunk-data to entity-body
3555 $new .= $chunk;
3556 $length += strlen($chunk);
3557 break;
3558 }
3559
3560 // read chunk-data and crlf
3561 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3562 // append chunk-data to entity-body
3563 $new .= $chunk;
3564 // length := length + chunk-size
3565 $length += strlen($chunk);
3566 // read chunk-size and crlf
3567 $chunkstart = $chunkend + 2;
3568
3569 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3570 if($chunkend == false)
3571 {
3572 break; //just in case we got a broken connection
3573 }
3574 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3575 $chunk_size = hexdec( trim($temp) );
3576 $chunkstart = $chunkend;
3577 }
3578 return $new;
3579 }
3580
3581 /**
3582 * xml charset encoding guessing helper function.
3583 * Tries to determine the charset encoding of an XML chunk
3584 * received over HTTP.
3585 * NB: according to the spec (RFC 3023, if text/xml content-type is received over HTTP without a content-type,
3586 * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3587 * which will be most probably using UTF-8 anyway...
3588 *
3589 * @param string $httpheaders the http Content-type header
3590 * @param string $xmlchunk xml content buffer
3591 * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3592 *
3593 * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3594 */
3595 function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3596 {
3597 // discussion: see http://www.yale.edu/pclt/encoding/
3598 // 1 - test if encoding is specified in HTTP HEADERS
3599
3600 //Details:
3601 // LWS: (\13\10)?( |\t)+
3602 // token: (any char but excluded stuff)+
3603 // header: Content-type = ...; charset=value(; ...)*
3604 // where value is of type token, no LWS allowed between 'charset' and value
3605 // Note: we do not check for invalid chars in VALUE:
3606 // this had better be done using pure ereg as below
3607
3608 /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3609 $matches = array();
3610 if(preg_match('/;\s*charset=([^;]+)/i', $httpheader, $matches))
3611 {
3612 return strtoupper(trim($matches[1]));
3613 }
3614
3615 // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3616 // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3617 // NOTE: actually, according to the spec, even if we find the BOM and determine
3618 // an encoding, we should check if there is an encoding specified
3619 // in the xml declaration, and verify if they match.
3620 /// @todo implement check as described above?
3621 /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3622 if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3623 {
3624 return 'UCS-4';
3625 }
3626 elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3627 {
3628 return 'UTF-16';
3629 }
3630 elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3631 {
3632 return 'UTF-8';
3633 }
3634
3635 // 3 - test if encoding is specified in the xml declaration
3636 // Details:
3637 // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3638 // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3639 if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3640 '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3641 $xmlchunk, $matches))
3642 {
3643 return strtoupper(substr($matches[2], 1, -1));
3644 }
3645
3646 // 4 - if mbstring is available, let it do the guesswork
3647 // NB: we favour finding an encoding that is compatible with what we can process
3648 if(extension_loaded('mbstring'))
3649 {
3650 if($encoding_prefs)
3651 {
3652 $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3653 }
3654 else
3655 {
3656 $enc = mb_detect_encoding($xmlchunk);
3657 }
3658 // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3659 // IANA also likes better US-ASCII, so go with it
3660 if($enc == 'ASCII')
3661 {
3662 $enc = 'US-'.$enc;
3663 }
3664 return $enc;
3665 }
3666 else
3667 {
3668 // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3669 // Both RFC 2616 (HTTP 1.1) and 1945(http 1.0) clearly state that for text/xxx content types
3670 // this should be the standard. And we should be getting text/xml as request and response.
3671 // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3672 return $GLOBALS['xmlrpc_defencoding'];
3673 }
3674 }
3675
3676 /**
3677 * Checks if a given charset encoding is present in a list of encodings or
3678 * if it is a valid subset of any encoding in the list
3679 * @param string $encoding charset to be tested
3680 * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3681 */
3682 function is_valid_charset($encoding, $validlist)
3683 {
3684 $charset_supersets = array(
3685 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3686 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3687 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3688 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3689 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3690 );
3691 if (is_string($validlist))
3692 $validlist = explode(',', $validlist);
3693 if (@in_array(strtoupper($encoding), $validlist))
3694 return true;
3695 else
3696 {
3697 if (array_key_exists($encoding, $charset_supersets))
3698 foreach ($validlist as $allowed)
3699 if (in_array($allowed, $charset_supersets[$encoding]))
3700 return true;
3701 return false;
3702 }
3703 }
3704
3705?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/xmlrpc_wrappers.inc b/update/plugin/subversion/trunk/xmlrpc/xmlrpc_wrappers.inc new file mode 100644 index 0000000..cd0a56f --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/xmlrpc_wrappers.inc
@@ -0,0 +1,819 @@
1<?php
2/**
3 * PHP-XMLRPC "wrapper" functions
4 * Generate stubs to transparently access xmlrpc methods as php functions and viceversa
5 *
6 * @version $Id: xmlrpc_wrappers.inc,v 1.12 2008/03/06 18:58:44 ggiunta Exp $
7 * @author Gaetano Giunta
8 * @copyright (C) 2006-2008 G. Giunta
9 * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
10 *
11 * @todo separate introspection from code generation for func-2-method wrapping
12 * @todo use some better templating system from code generation?
13 * @todo implement method wrapping with preservation of php objs in calls
14 * @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
15 * @todo implement self-parsing of php code for PHP <= 4
16 */
17
18 // requires: xmlrpc.inc
19
20 /**
21 * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22 * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23 * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24 * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25 * @param string $phptype
26 * @return string
27 */
28 function php_2_xmlrpc_type($phptype)
29 {
30 switch(strtolower($phptype))
31 {
32 case 'string':
33 return $GLOBALS['xmlrpcString'];
34 case 'integer':
35 case $GLOBALS['xmlrpcInt']: // 'int'
36 case $GLOBALS['xmlrpcI4']:
37 return $GLOBALS['xmlrpcInt'];
38 case 'double':
39 return $GLOBALS['xmlrpcDouble'];
40 case 'boolean':
41 return $GLOBALS['xmlrpcBoolean'];
42 case 'array':
43 return $GLOBALS['xmlrpcArray'];
44 case 'object':
45 return $GLOBALS['xmlrpcStruct'];
46 case $GLOBALS['xmlrpcBase64']:
47 case $GLOBALS['xmlrpcStruct']:
48 return strtolower($phptype);
49 case 'resource':
50 return '';
51 default:
52 if(class_exists($phptype))
53 {
54 return $GLOBALS['xmlrpcStruct'];
55 }
56 else
57 {
58 // unknown: might be any 'extended' xmlrpc type
59 return $GLOBALS['xmlrpcValue'];
60 }
61 }
62 }
63
64 /**
65 * Given a string defining a phpxmlrpc type return corresponding php type.
66 * @param string $xmlrpctype
67 * @return string
68 */
69 function xmlrpc_2_php_type($xmlrpctype)
70 {
71 switch(strtolower($xmlrpctype))
72 {
73 case 'base64':
74 case 'datetime.iso8601':
75 case 'string':
76 return $GLOBALS['xmlrpcString'];
77 case 'int':
78 case 'i4':
79 return 'integer';
80 case 'struct':
81 case 'array':
82 return 'array';
83 case 'double':
84 return 'float';
85 case 'undefined':
86 return 'mixed';
87 case 'boolean':
88 case 'null':
89 default:
90 // unknown: might be any xmlrpc type
91 return strtolower($xmlrpctype);
92 }
93 }
94
95 /**
96 * Given a user-defined PHP function, create a PHP 'wrapper' function that can
97 * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
98 * clients (as well as its corresponding signature info).
99 *
100 * Since php is a typeless language, to infer types of input and output parameters,
101 * it relies on parsing the javadoc-style comment block associated with the given
102 * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
103 * in the @param tag is also allowed, if you need the php function to receive/send
104 * data in that particular format (note that base64 encoding/decoding is transparently
105 * carried out by the lib, while datetime vals are passed around as strings)
106 *
107 * Known limitations:
108 * - requires PHP 5.0.3 +
109 * - only works for user-defined functions, not for PHP internal functions
110 * (reflection does not support retrieving number/type of params for those)
111 * - functions returning php objects will generate special xmlrpc responses:
112 * when the xmlrpc decoding of those responses is carried out by this same lib, using
113 * the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
114 * In short: php objects can be serialized, too (except for their resource members),
115 * using this function.
116 * Other libs might choke on the very same xml that will be generated in this case
117 * (i.e. it has a nonstandard attribute on struct element tags)
118 * - usage of javadoc @param tags using param names in a different order from the
119 * function prototype is not considered valid (to be fixed?)
120 *
121 * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
122 * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
123 * is by making use of the functions_parameters_type class member.
124 *
125 * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') might be ok too, in the future...
126 * @param string $newfuncname (optional) name for function to be created
127 * @param array $extra_options (optional) array of options for conversion. valid values include:
128 * bool return_source when true, php code w. function definition will be returned, not evaluated
129 * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
130 * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
131 * bool suppress_warnings remove from produced xml any runtime warnings due to the php function being invoked
132 * @return false on error, or an array containing the name of the new php function,
133 * its signature and docs, to be used in the server dispatch map
134 *
135 * @todo decide how to deal with params passed by ref: bomb out or allow?
136 * @todo finish using javadoc info to build method sig if all params are named but out of order
137 * @todo add a check for params of 'resource' type
138 * @todo add some trigger_errors / error_log when returning false?
139 * @todo what to do when the PHP function returns NULL? we are currently an empty string value...
140 * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
141 */
142 function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
143 {
144 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
145 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
146 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
147 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
148 $catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
149
150 if(version_compare(phpversion(), '5.0.3') == -1)
151 {
152 // up to php 5.0.3 some useful reflection methods were missing
153 error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
154 return false;
155 }
156 if((is_array($funcname) && !method_exists($funcname[0], $funcname[1])) || !function_exists($funcname))
157 {
158 error_log('XML-RPC: function to be wrapped is not defined: '.$funcname);
159 return false;
160 }
161 else
162 {
163 // determine name of new php function
164 if($newfuncname == '')
165 {
166 if(is_array($funcname))
167 {
168 $xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
169 }
170 else
171 {
172 $xmlrpcfuncname = "{$prefix}_$funcname";
173 }
174 }
175 else
176 {
177 $xmlrpcfuncname = $newfuncname;
178 }
179 while($buildit && function_exists($xmlrpcfuncname))
180 {
181 $xmlrpcfuncname .= 'x';
182 }
183
184 // start to introspect PHP code
185 $func =& new ReflectionFunction($funcname);
186 if($func->isInternal())
187 {
188 // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
189 // instead of getparameters to fully reflect internal php functions ?
190 error_log('XML-RPC: function to be wrapped is internal: '.$funcname);
191 return false;
192 }
193
194 // retrieve parameter names, types and description from javadoc comments
195
196 // function description
197 $desc = '';
198 // type of return val: by default 'any'
199 $returns = $GLOBALS['xmlrpcValue'];
200 // desc of return val
201 $returnsDocs = '';
202 // type + name of function parameters
203 $paramDocs = array();
204
205 $docs = $func->getDocComment();
206 if($docs != '')
207 {
208 $docs = explode("\n", $docs);
209 $i = 0;
210 foreach($docs as $doc)
211 {
212 $doc = trim($doc, " \r\t/*");
213 if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
214 {
215 if($desc)
216 {
217 $desc .= "\n";
218 }
219 $desc .= $doc;
220 }
221 elseif(strpos($doc, '@param') === 0)
222 {
223 // syntax: @param type [$name] desc
224 if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
225 {
226 if(strpos($matches[1], '|'))
227 {
228 //$paramDocs[$i]['type'] = explode('|', $matches[1]);
229 $paramDocs[$i]['type'] = 'mixed';
230 }
231 else
232 {
233 $paramDocs[$i]['type'] = $matches[1];
234 }
235 $paramDocs[$i]['name'] = trim($matches[2]);
236 $paramDocs[$i]['doc'] = $matches[3];
237 }
238 $i++;
239 }
240 elseif(strpos($doc, '@return') === 0)
241 {
242 // syntax: @return type desc
243 //$returns = preg_split('/\s+/', $doc);
244 if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
245 {
246 $returns = php_2_xmlrpc_type($matches[1]);
247 if(isset($matches[2]))
248 {
249 $returnsDocs = $matches[2];
250 }
251 }
252 }
253 }
254 }
255
256 // execute introspection of actual function prototype
257 $params = array();
258 $i = 0;
259 foreach($func->getParameters() as $paramobj)
260 {
261 $params[$i] = array();
262 $params[$i]['name'] = '$'.$paramobj->getName();
263 $params[$i]['isoptional'] = $paramobj->isOptional();
264 $i++;
265 }
266
267
268 // start building of PHP code to be eval'd
269 $innercode = '';
270 $i = 0;
271 $parsvariations = array();
272 $pars = array();
273 $pnum = count($params);
274 foreach($params as $param)
275 {
276 if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
277 {
278 // param name from phpdoc info does not match param definition!
279 $paramDocs[$i]['type'] = 'mixed';
280 }
281
282 if($param['isoptional'])
283 {
284 // this particular parameter is optional. save as valid previous list of parameters
285 $innercode .= "if (\$paramcount > $i) {\n";
286 $parsvariations[] = $pars;
287 }
288 $innercode .= "\$p$i = \$msg->getParam($i);\n";
289 if ($decode_php_objects)
290 {
291 $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
292 }
293 else
294 {
295 $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
296 }
297
298 $pars[] = "\$p$i";
299 $i++;
300 if($param['isoptional'])
301 {
302 $innercode .= "}\n";
303 }
304 if($i == $pnum)
305 {
306 // last allowed parameters combination
307 $parsvariations[] = $pars;
308 }
309 }
310
311 $sigs = array();
312 $psigs = array();
313 if(count($parsvariations) == 0)
314 {
315 // only known good synopsis = no parameters
316 $parsvariations[] = array();
317 $minpars = 0;
318 }
319 else
320 {
321 $minpars = count($parsvariations[0]);
322 }
323
324 if($minpars)
325 {
326 // add to code the check for min params number
327 // NB: this check needs to be done BEFORE decoding param values
328 $innercode = "\$paramcount = \$msg->getNumParams();\n" .
329 "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
330 }
331 else
332 {
333 $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
334 }
335
336 $innercode .= "\$np = false;\n";
337 foreach($parsvariations as $pars)
338 {
339 $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$funcname(" . implode(',', $pars) . "); else\n";
340 // build a 'generic' signature (only use an appropriate return type)
341 $sig = array($returns);
342 $psig = array($returnsDocs);
343 for($i=0; $i < count($pars); $i++)
344 {
345 if (isset($paramDocs[$i]['type']))
346 {
347 $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
348 }
349 else
350 {
351 $sig[] = $GLOBALS['xmlrpcValue'];
352 }
353 $psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
354 }
355 $sigs[] = $sig;
356 $psigs[] = $psig;
357 }
358 $innercode .= "\$np = true;\n";
359 $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
360 //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
361 $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
362 if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
363 {
364 $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
365 }
366 else
367 {
368 if ($encode_php_objects)
369 $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
370 else
371 $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
372 }
373 // shall we exclude functions returning by ref?
374 // if($func->returnsReference())
375 // return false;
376 $code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
377 //print_r($code);
378 if ($buildit)
379 {
380 $allOK = 0;
381 eval($code.'$allOK=1;');
382 // alternative
383 //$xmlrpcfuncname = create_function('$m', $innercode);
384
385 if(!$allOK)
386 {
387 error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$funcname);
388 return false;
389 }
390 }
391
392 /// @todo examine if $paramDocs matches $parsvariations and build array for
393 /// usage as method signature, plus put together a nice string for docs
394
395 $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
396 return $ret;
397 }
398 }
399
400 /**
401 * Given an xmlrpc client and a method name, register a php wrapper function
402 * that will call it and return results using native php types for both
403 * params and results. The generated php function will return an xmlrpcresp
404 * oject for failed xmlrpc calls
405 *
406 * Known limitations:
407 * - server must support system.methodsignature for the wanted xmlrpc method
408 * - for methods that expose many signatures, only one can be picked (we
409 * could in priciple check if signatures differ only by number of params
410 * and not by type, but it would be more complication than we can spare time)
411 * - nested xmlrpc params: the caller of the generated php function has to
412 * encode on its own the params passed to the php function if these are structs
413 * or arrays whose (sub)members include values of type datetime or base64
414 *
415 * Notes: the connection properties of the given client will be copied
416 * and reused for the connection used during the call to the generated
417 * php function.
418 * Calling the generated php function 'might' be slow: a new xmlrpc client
419 * is created on every invocation and an xmlrpc-connection opened+closed.
420 * An extra 'debug' param is appended to param list of xmlrpc method, useful
421 * for debugging purposes.
422 *
423 * @param xmlrpc_client $client an xmlrpc client set up correctly to communicate with target server
424 * @param string $methodname the xmlrpc method to be mapped to a php function
425 * @param array $extra_options array of options that specify conversion details. valid ptions include
426 * integer signum the index of the method signature to use in mapping (if method exposes many sigs)
427 * integer timeout timeout (in secs) to be used when executing function/calling remote method
428 * string protocol 'http' (default), 'http11' or 'https'
429 * string new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
430 * string return_source if true return php code w. function definition instead fo function name
431 * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
432 * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
433 * mixed return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
434 * bool debug set it to 1 or 2 to see debug results of querying server for method synopsis
435 * @return string the name of the generated php function (or false) - OR AN ARRAY...
436 */
437 function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
438 {
439 // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
440 // OR the 2.0 calling convention (no ptions) - we really love backward compat, don't we?
441 if (!is_array($extra_options))
442 {
443 $signum = $extra_options;
444 $extra_options = array();
445 }
446 else
447 {
448 $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
449 $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
450 $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
451 $newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
452 }
453 //$encode_php_objects = in_array('encode_php_objects', $extra_options);
454 //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
455 // in_array('build_class_code', $extra_options) ? 2 : 0;
456
457 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
458 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
459 $simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
460 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
461 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
462 if (isset($extra_options['return_on_fault']))
463 {
464 $decode_fault = true;
465 $fault_response = $extra_options['return_on_fault'];
466 }
467 else
468 {
469 $decode_fault = false;
470 $fault_response = '';
471 }
472 $debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
473
474 $msgclass = $prefix.'msg';
475 $valclass = $prefix.'val';
476 $decodefunc = 'php_'.$prefix.'_decode';
477
478 $msg =& new $msgclass('system.methodSignature');
479 $msg->addparam(new $valclass($methodname));
480 $client->setDebug($debug);
481 $response =& $client->send($msg, $timeout, $protocol);
482 if($response->faultCode())
483 {
484 error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
485 return false;
486 }
487 else
488 {
489 $msig = $response->value();
490 if ($client->return_type != 'phpvals')
491 {
492 $msig = $decodefunc($msig);
493 }
494 if(!is_array($msig) || count($msig) <= $signum)
495 {
496 error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
497 return false;
498 }
499 else
500 {
501 // pick a suitable name for the new function, avoiding collisions
502 if($newfuncname != '')
503 {
504 $xmlrpcfuncname = $newfuncname;
505 }
506 else
507 {
508 // take care to insure that methodname is translated to valid
509 // php function name
510 $xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
511 array('_', ''), $methodname);
512 }
513 while($buildit && function_exists($xmlrpcfuncname))
514 {
515 $xmlrpcfuncname .= 'x';
516 }
517
518 $msig = $msig[$signum];
519 $mdesc = '';
520 // if in 'offline' mode, get method description too.
521 // in online mode, favour speed of operation
522 if(!$buildit)
523 {
524 $msg =& new $msgclass('system.methodHelp');
525 $msg->addparam(new $valclass($methodname));
526 $response =& $client->send($msg, $timeout, $protocol);
527 if (!$response->faultCode())
528 {
529 $mdesc = $response->value();
530 if ($client->return_type != 'phpvals')
531 {
532 $mdesc = $mdesc->scalarval();
533 }
534 }
535 }
536
537 $results = build_remote_method_wrapper_code($client, $methodname,
538 $xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
539 $prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
540 $fault_response);
541
542 //print_r($code);
543 if ($buildit)
544 {
545 $allOK = 0;
546 eval($results['source'].'$allOK=1;');
547 // alternative
548 //$xmlrpcfuncname = create_function('$m', $innercode);
549 if($allOK)
550 {
551 return $xmlrpcfuncname;
552 }
553 else
554 {
555 error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
556 return false;
557 }
558 }
559 else
560 {
561 $results['function'] = $xmlrpcfuncname;
562 return $results;
563 }
564 }
565 }
566 }
567
568 /**
569 * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
570 * all xmlrpc methods exposed by the remote server as own methods.
571 * For more details see wrap_xmlrpc_method.
572 * @param xmlrpc_client $client the client obj all set to query the desired server
573 * @param array $extra_options list of options for wrapped code
574 * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
575 */
576 function wrap_xmlrpc_server($client, $extra_options=array())
577 {
578 $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
579 $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
580 $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
581 $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
582 $newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
583 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
584 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
585 $verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
586 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
587 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
588
589 $msgclass = $prefix.'msg';
590 //$valclass = $prefix.'val';
591 $decodefunc = 'php_'.$prefix.'_decode';
592
593 $msg =& new $msgclass('system.listMethods');
594 $response =& $client->send($msg, $timeout, $protocol);
595 if($response->faultCode())
596 {
597 error_log('XML-RPC: could not retrieve method list from remote server');
598 return false;
599 }
600 else
601 {
602 $mlist = $response->value();
603 if ($client->return_type != 'phpvals')
604 {
605 $mlist = $decodefunc($mlist);
606 }
607 if(!is_array($mlist) || !count($mlist))
608 {
609 error_log('XML-RPC: could not retrieve meaningful method list from remote server');
610 return false;
611 }
612 else
613 {
614 // pick a suitable name for the new function, avoiding collisions
615 if($newclassname != '')
616 {
617 $xmlrpcclassname = $newclassname;
618 }
619 else
620 {
621 $xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
622 array('_', ''), $client->server).'_client';
623 }
624 while($buildit && class_exists($xmlrpcclassname))
625 {
626 $xmlrpcclassname .= 'x';
627 }
628
629 /// @todo add function setdebug() to new class, to enable/disable debugging
630 $source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
631 $source .= "function $xmlrpcclassname()\n{\n";
632 $source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
633 $source .= "\$this->client =& \$client;\n}\n\n";
634 $opts = array('simple_client_copy' => 2, 'return_source' => true,
635 'timeout' => $timeout, 'protocol' => $protocol,
636 'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
637 'decode_php_objs' => $decode_php_objects
638 );
639 /// @todo build javadoc for class definition, too
640 foreach($mlist as $mname)
641 {
642 if ($methodfilter == '' || preg_match($methodfilter, $mname))
643 {
644 $opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
645 array('_', ''), $mname);
646 $methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
647 if ($methodwrap)
648 {
649 if (!$buildit)
650 {
651 $source .= $methodwrap['docstring'];
652 }
653 $source .= $methodwrap['source']."\n";
654 }
655 else
656 {
657 error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
658 }
659 }
660 }
661 $source .= "}\n";
662 if ($buildit)
663 {
664 $allOK = 0;
665 eval($source.'$allOK=1;');
666 // alternative
667 //$xmlrpcfuncname = create_function('$m', $innercode);
668 if($allOK)
669 {
670 return $xmlrpcclassname;
671 }
672 else
673 {
674 error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
675 return false;
676 }
677 }
678 else
679 {
680 return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
681 }
682 }
683 }
684 }
685
686 /**
687 * Given the necessary info, build php code that creates a new function to
688 * invoke a remote xmlrpc method.
689 * Take care that no full checking of input parameters is done to ensure that
690 * valid php code is emitted.
691 * Note: real spaghetti code follows...
692 * @access private
693 */
694 function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
695 $msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
696 $decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
697 $fault_response='')
698 {
699 $code = "function $xmlrpcfuncname (";
700 if ($client_copy_mode < 2)
701 {
702 // client copy mode 0 or 1 == partial / full client copy in emitted code
703 $innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
704 $innercode .= "\$client->setDebug(\$debug);\n";
705 $this_ = '';
706 }
707 else
708 {
709 // client copy mode 2 == no client copy in emitted code
710 $innercode = '';
711 $this_ = 'this->';
712 }
713 $innercode .= "\$msg =& new {$prefix}msg('$methodname');\n";
714
715 if ($mdesc != '')
716 {
717 // take care that PHP comment is not terminated unwillingly by method description
718 $mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
719 }
720 else
721 {
722 $mdesc = "/**\nFunction $xmlrpcfuncname\n";
723 }
724
725 // param parsing
726 $plist = array();
727 $pcount = count($msig);
728 for($i = 1; $i < $pcount; $i++)
729 {
730 $plist[] = "\$p$i";
731 $ptype = $msig[$i];
732 if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
733 $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
734 {
735 // only build directly xmlrpcvals when type is known and scalar
736 $innercode .= "\$p$i =& new {$prefix}val(\$p$i, '$ptype');\n";
737 }
738 else
739 {
740 if ($encode_php_objects)
741 {
742 $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
743 }
744 else
745 {
746 $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
747 }
748 }
749 $innercode .= "\$msg->addparam(\$p$i);\n";
750 $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
751 }
752 if ($client_copy_mode < 2)
753 {
754 $plist[] = '$debug=0';
755 $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
756 }
757 $plist = implode(', ', $plist);
758 $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
759
760 $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
761 if ($decode_fault)
762 {
763 if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
764 {
765 $respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
766 }
767 else
768 {
769 $respcode = var_export($fault_response, true);
770 }
771 }
772 else
773 {
774 $respcode = '$res';
775 }
776 if ($decode_php_objects)
777 {
778 $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
779 }
780 else
781 {
782 $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
783 }
784
785 $code = $code . $plist. ") {\n" . $innercode . "\n}\n";
786
787 return array('source' => $code, 'docstring' => $mdesc);
788 }
789
790 /**
791 * Given necessary info, generate php code that will rebuild a client object
792 * Take care that no full checking of input parameters is done to ensure that
793 * valid php code is emitted.
794 * @access private
795 */
796 function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
797 {
798 $code = "\$client =& new {$prefix}_client('".str_replace("'", "\'", $client->path).
799 "', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
800
801 // copy all client fields to the client that will be generated runtime
802 // (this provides for future expansion or subclassing of client obj)
803 if ($verbatim_client_copy)
804 {
805 foreach($client as $fld => $val)
806 {
807 if($fld != 'debug' && $fld != 'return_type')
808 {
809 $val = var_export($val, true);
810 $code .= "\$client->$fld = $val;\n";
811 }
812 }
813 }
814 // only make sure that client always returns the correct data type
815 $code .= "\$client->return_type = '{$prefix}vals';\n";
816 //$code .= "\$client->setDebug(\$debug);\n";
817 return $code;
818 }
819?> \ No newline at end of file
diff --git a/update/plugin/subversion/trunk/xmlrpc/xmlrpcs.inc b/update/plugin/subversion/trunk/xmlrpc/xmlrpcs.inc new file mode 100644 index 0000000..9588919 --- /dev/null +++ b/update/plugin/subversion/trunk/xmlrpc/xmlrpcs.inc
@@ -0,0 +1,1193 @@
1<?php
2// by Edd Dumbill (C) 1999-2002
3// <edd@usefulinc.com>
4// $Id: xmlrpcs.inc,v 1.69 2007/09/20 20:14:25 ggiunta Exp $
5
6// Copyright (c) 1999,2000,2002 Edd Dumbill.
7// All rights reserved.
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions
11// are met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// * Redistributions in binary form must reproduce the above
17// copyright notice, this list of conditions and the following
18// disclaimer in the documentation and/or other materials provided
19// with the distribution.
20//
21// * Neither the name of the "XML-RPC for PHP" nor the names of its
22// contributors may be used to endorse or promote products derived
23// from this software without specific prior written permission.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36// OF THE POSSIBILITY OF SUCH DAMAGE.
37
38 // XML RPC Server class
39 // requires: xmlrpc.inc
40
41 $GLOBALS['xmlrpcs_capabilities'] = array(
42 // xmlrpc spec: always supported
43 'xmlrpc' => new xmlrpcval(array(
44 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
45 'specVersion' => new xmlrpcval(1, 'int')
46 ), 'struct'),
47 // if we support system.xxx functions, we always support multicall, too...
48 // Note that, as of 2006/09/17, the following URL does not respond anymore
49 'system.multicall' => new xmlrpcval(array(
50 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
51 'specVersion' => new xmlrpcval(1, 'int')
52 ), 'struct'),
53 // introspection: version 2! we support 'mixed', too
54 'introspection' => new xmlrpcval(array(
55 'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
56 'specVersion' => new xmlrpcval(2, 'int')
57 ), 'struct')
58 );
59
60 /* Functions that implement system.XXX methods of xmlrpc servers */
61 $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
62 $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
63 $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
64 function _xmlrpcs_getCapabilities($server, $m=null)
65 {
66 $outAr = $GLOBALS['xmlrpcs_capabilities'];
67 // NIL extension
68 if ($GLOBALS['xmlrpc_null_extension']) {
69 $outAr['nil'] = new xmlrpcval(array(
70 'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
71 'specVersion' => new xmlrpcval(1, 'int')
72 ), 'struct');
73 }
74 return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
75 }
76
77 // listMethods: signature was either a string, or nothing.
78 // The useless string variant has been removed
79 $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
80 $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
81 $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
82 function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
83 {
84
85 $outAr=array();
86 foreach($server->dmap as $key => $val)
87 {
88 $outAr[]=&new xmlrpcval($key, 'string');
89 }
90 if($server->allow_system_funcs)
91 {
92 foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
93 {
94 $outAr[]=&new xmlrpcval($key, 'string');
95 }
96 }
97 return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
98 }
99
100 $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
101 $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
102 $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
103 function _xmlrpcs_methodSignature($server, $m)
104 {
105 // let accept as parameter both an xmlrpcval or string
106 if (is_object($m))
107 {
108 $methName=$m->getParam(0);
109 $methName=$methName->scalarval();
110 }
111 else
112 {
113 $methName=$m;
114 }
115 if(strpos($methName, "system.") === 0)
116 {
117 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
118 }
119 else
120 {
121 $dmap=$server->dmap; $sysCall=0;
122 }
123 if(isset($dmap[$methName]))
124 {
125 if(isset($dmap[$methName]['signature']))
126 {
127 $sigs=array();
128 foreach($dmap[$methName]['signature'] as $inSig)
129 {
130 $cursig=array();
131 foreach($inSig as $sig)
132 {
133 $cursig[]=&new xmlrpcval($sig, 'string');
134 }
135 $sigs[]=&new xmlrpcval($cursig, 'array');
136 }
137 $r=&new xmlrpcresp(new xmlrpcval($sigs, 'array'));
138 }
139 else
140 {
141 // NB: according to the official docs, we should be returning a
142 // "none-array" here, which means not-an-array
143 $r=&new xmlrpcresp(new xmlrpcval('undef', 'string'));
144 }
145 }
146 else
147 {
148 $r=&new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
149 }
150 return $r;
151 }
152
153 $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
154 $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
155 $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
156 function _xmlrpcs_methodHelp($server, $m)
157 {
158 // let accept as parameter both an xmlrpcval or string
159 if (is_object($m))
160 {
161 $methName=$m->getParam(0);
162 $methName=$methName->scalarval();
163 }
164 else
165 {
166 $methName=$m;
167 }
168 if(strpos($methName, "system.") === 0)
169 {
170 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
171 }
172 else
173 {
174 $dmap=$server->dmap; $sysCall=0;
175 }
176 if(isset($dmap[$methName]))
177 {
178 if(isset($dmap[$methName]['docstring']))
179 {
180 $r=&new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
181 }
182 else
183 {
184 $r=&new xmlrpcresp(new xmlrpcval('', 'string'));
185 }
186 }
187 else
188 {
189 $r=&new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
190 }
191 return $r;
192 }
193
194 $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
195 $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
196 $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
197 function _xmlrpcs_multicall_error($err)
198 {
199 if(is_string($err))
200 {
201 $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
202 $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
203 }
204 else
205 {
206 $code = $err->faultCode();
207 $str = $err->faultString();
208 }
209 $struct = array();
210 $struct['faultCode'] =& new xmlrpcval($code, 'int');
211 $struct['faultString'] =& new xmlrpcval($str, 'string');
212 return new xmlrpcval($struct, 'struct');
213 }
214
215 function _xmlrpcs_multicall_do_call($server, $call)
216 {
217 if($call->kindOf() != 'struct')
218 {
219 return _xmlrpcs_multicall_error('notstruct');
220 }
221 $methName = @$call->structmem('methodName');
222 if(!$methName)
223 {
224 return _xmlrpcs_multicall_error('nomethod');
225 }
226 if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
227 {
228 return _xmlrpcs_multicall_error('notstring');
229 }
230 if($methName->scalarval() == 'system.multicall')
231 {
232 return _xmlrpcs_multicall_error('recursion');
233 }
234
235 $params = @$call->structmem('params');
236 if(!$params)
237 {
238 return _xmlrpcs_multicall_error('noparams');
239 }
240 if($params->kindOf() != 'array')
241 {
242 return _xmlrpcs_multicall_error('notarray');
243 }
244 $numParams = $params->arraysize();
245
246 $msg =& new xmlrpcmsg($methName->scalarval());
247 for($i = 0; $i < $numParams; $i++)
248 {
249 if(!$msg->addParam($params->arraymem($i)))
250 {
251 $i++;
252 return _xmlrpcs_multicall_error(new xmlrpcresp(0,
253 $GLOBALS['xmlrpcerr']['incorrect_params'],
254 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
255 }
256 }
257
258 $result = $server->execute($msg);
259
260 if($result->faultCode() != 0)
261 {
262 return _xmlrpcs_multicall_error($result); // Method returned fault.
263 }
264
265 return new xmlrpcval(array($result->value()), 'array');
266 }
267
268 function _xmlrpcs_multicall_do_call_phpvals($server, $call)
269 {
270 if(!is_array($call))
271 {
272 return _xmlrpcs_multicall_error('notstruct');
273 }
274 if(!array_key_exists('methodName', $call))
275 {
276 return _xmlrpcs_multicall_error('nomethod');
277 }
278 if (!is_string($call['methodName']))
279 {
280 return _xmlrpcs_multicall_error('notstring');
281 }
282 if($call['methodName'] == 'system.multicall')
283 {
284 return _xmlrpcs_multicall_error('recursion');
285 }
286 if(!array_key_exists('params', $call))
287 {
288 return _xmlrpcs_multicall_error('noparams');
289 }
290 if(!is_array($call['params']))
291 {
292 return _xmlrpcs_multicall_error('notarray');
293 }
294
295 // this is a real dirty and simplistic hack, since we might have received a
296 // base64 or datetime values, but they will be listed as strings here...
297 $numParams = count($call['params']);
298 $pt = array();
299 foreach($call['params'] as $val)
300 $pt[] = php_2_xmlrpc_type(gettype($val));
301
302 $result = $server->execute($call['methodName'], $call['params'], $pt);
303
304 if($result->faultCode() != 0)
305 {
306 return _xmlrpcs_multicall_error($result); // Method returned fault.
307 }
308
309 return new xmlrpcval(array($result->value()), 'array');
310 }
311
312 function _xmlrpcs_multicall($server, $m)
313 {
314 $result = array();
315 // let accept a plain list of php parameters, beside a single xmlrpc msg object
316 if (is_object($m))
317 {
318 $calls = $m->getParam(0);
319 $numCalls = $calls->arraysize();
320 for($i = 0; $i < $numCalls; $i++)
321 {
322 $call = $calls->arraymem($i);
323 $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
324 }
325 }
326 else
327 {
328 $numCalls=count($m);
329 for($i = 0; $i < $numCalls; $i++)
330 {
331 $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
332 }
333 }
334
335 return new xmlrpcresp(new xmlrpcval($result, 'array'));
336 }
337
338 $GLOBALS['_xmlrpcs_dmap']=array(
339 'system.listMethods' => array(
340 'function' => '_xmlrpcs_listMethods',
341 'signature' => $_xmlrpcs_listMethods_sig,
342 'docstring' => $_xmlrpcs_listMethods_doc,
343 'signature_docs' => $_xmlrpcs_listMethods_sdoc),
344 'system.methodHelp' => array(
345 'function' => '_xmlrpcs_methodHelp',
346 'signature' => $_xmlrpcs_methodHelp_sig,
347 'docstring' => $_xmlrpcs_methodHelp_doc,
348 'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
349 'system.methodSignature' => array(
350 'function' => '_xmlrpcs_methodSignature',
351 'signature' => $_xmlrpcs_methodSignature_sig,
352 'docstring' => $_xmlrpcs_methodSignature_doc,
353 'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
354 'system.multicall' => array(
355 'function' => '_xmlrpcs_multicall',
356 'signature' => $_xmlrpcs_multicall_sig,
357 'docstring' => $_xmlrpcs_multicall_doc,
358 'signature_docs' => $_xmlrpcs_multicall_sdoc),
359 'system.getCapabilities' => array(
360 'function' => '_xmlrpcs_getCapabilities',
361 'signature' => $_xmlrpcs_getCapabilities_sig,
362 'docstring' => $_xmlrpcs_getCapabilities_doc,
363 'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
364 );
365
366 $GLOBALS['_xmlrpcs_occurred_errors'] = '';
367 $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
368 /**
369 * Error handler used to track errors that occur during server-side execution of PHP code.
370 * This allows to report back to the client whether an internal error has occurred or not
371 * using an xmlrpc response object, instead of letting the client deal with the html junk
372 * that a PHP execution error on the server generally entails.
373 *
374 * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
375 *
376 */
377 function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
378 {
379 // obey the @ protocol
380 if (error_reporting() == 0)
381 return;
382
383 //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
384 if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined
385 {
386 $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
387 }
388 // Try to avoid as much as possible disruption to the previous error handling
389 // mechanism in place
390 if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
391 {
392 // The previous error handler was the default: all we should do is log error
393 // to the default error log (if level high enough)
394 if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
395 {
396 error_log($errstring);
397 }
398 }
399 else
400 {
401 // Pass control on to previous error handler, trying to avoid loops...
402 if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
403 {
404 // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
405 if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
406 {
407 // the following works both with static class methods and plain object methods as error handler
408 call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
409 }
410 else
411 {
412 $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
413 }
414 }
415 }
416 }
417
418 $GLOBALS['_xmlrpc_debuginfo']='';
419
420 /**
421 * Add a string to the debug info that can be later seralized by the server
422 * as part of the response message.
423 * Note that for best compatbility, the debug string should be encoded using
424 * the $GLOBALS['xmlrpc_internalencoding'] character set.
425 * @param string $m
426 * @access public
427 */
428 function xmlrpc_debugmsg($m)
429 {
430 $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
431 }
432
433 class xmlrpc_server
434 {
435 /// array defining php functions exposed as xmlrpc methods by this server
436 var $dmap=array();
437 /**
438 * Defines how functions in dmap will be invokde: either using an xmlrpc msg object
439 * or plain php values.
440 * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
441 */
442 var $functions_parameters_type='xmlrpcvals';
443 /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
444 var $debug = 1;
445 /**
446 * When set to true, it will enable HTTP compression of the response, in case
447 * the client has declared its support for compression in the request.
448 */
449 var $compress_response = false;
450 /**
451 * List of http compression methods accepted by the server for requests.
452 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
453 */
454 var $accepted_compression = array();
455 /// shall we serve calls to system.* methods?
456 var $allow_system_funcs = true;
457 /// list of charset encodings natively accepted for requests
458 var $accepted_charset_encodings = array();
459 /**
460 * charset encoding to be used for response.
461 * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
462 * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
463 * null (leave unspecified in response, convert output stream to US_ASCII),
464 * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
465 * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
466 * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
467 */
468 var $response_charset_encoding = '';
469 /// storage for internal debug info
470 var $debug_info = '';
471 /// extra data passed at runtime to method handling functions. Used only by EPI layer
472 var $user_data = null;
473
474 /**
475 * @param array $dispmap the dispatch map withd efinition of exposed services
476 * @param boolean $servicenow set to false to prevent the server from runnung upon construction
477 */
478 function xmlrpc_server($dispMap=null, $serviceNow=true)
479 {
480 // if ZLIB is enabled, let the server by default accept compressed requests,
481 // and compress responses sent to clients that support them
482 if(function_exists('gzinflate'))
483 {
484 $this->accepted_compression = array('gzip', 'deflate');
485 $this->compress_response = true;
486 }
487
488 // by default the xml parser can support these 3 charset encodings
489 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
490
491 // dispMap is a dispatch array of methods
492 // mapped to function names and signatures
493 // if a method
494 // doesn't appear in the map then an unknown
495 // method error is generated
496 /* milosch - changed to make passing dispMap optional.
497 * instead, you can use the class add_to_map() function
498 * to add functions manually (borrowed from SOAPX4)
499 */
500 if($dispMap)
501 {
502 $this->dmap = $dispMap;
503 if($serviceNow)
504 {
505 $this->service();
506 }
507 }
508 }
509
510 /**
511 * Set debug level of server.
512 * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
513 * 0 = no debug info,
514 * 1 = msgs set from user with debugmsg(),
515 * 2 = add complete xmlrpc request (headers and body),
516 * 3 = add also all processing warnings happened during method processing
517 * (NB: this involves setting a custom error handler, and might interfere
518 * with the standard processing of the php function exposed as method. In
519 * particular, triggering an USER_ERROR level error will not halt script
520 * execution anymore, but just end up logged in the xmlrpc response)
521 * Note that info added at elevel 2 and 3 will be base64 encoded
522 * @access public
523 */
524 function setDebug($in)
525 {
526 $this->debug=$in;
527 }
528
529 /**
530 * Return a string with the serialized representation of all debug info
531 * @param string $charset_encoding the target charset encoding for the serialization
532 * @return string an XML comment (or two)
533 */
534 function serializeDebug($charset_encoding='')
535 {
536 // Tough encoding problem: which internal charset should we assume for debug info?
537 // It might contain a copy of raw data received from client, ie with unknown encoding,
538 // intermixed with php generated data and user generated data...
539 // so we split it: system debug is base 64 encoded,
540 // user debug info should be encoded by the end user using the INTERNAL_ENCODING
541 $out = '';
542 if ($this->debug_info != '')
543 {
544 $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
545 }
546 if($GLOBALS['_xmlrpc_debuginfo']!='')
547 {
548
549 $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
550 // NB: a better solution MIGHT be to use CDATA, but we need to insert it
551 // into return payload AFTER the beginning tag
552 //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
553 }
554 return $out;
555 }
556
557 /**
558 * Execute the xmlrpc request, printing the response
559 * @param string $data the request body. If null, the http POST request will be examined
560 * @return xmlrpcresp the response object (usually not used by caller...)
561 * @access public
562 */
563 function service($data=null, $return_payload=false)
564 {
565 if ($data === null)
566 {
567 // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
568 $ver = phpversion();
569 if ($ver[0] >= 5)
570 {
571 $data = file_get_contents('php://input');
572 }
573 else
574 {
575 $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
576 }
577 }
578 $raw_data = $data;
579
580 // reset internal debug info
581 $this->debug_info = '';
582
583 // Echo back what we received, before parsing it
584 if($this->debug > 1)
585 {
586 $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
587 }
588
589 $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
590 if (!$r)
591 {
592 $r=$this->parseRequest($data, $req_charset);
593 }
594
595 // save full body of request into response, for more debugging usages
596 $r->raw_data = $raw_data;
597
598 if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
599 {
600 $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
601 $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
602 }
603
604 $payload=$this->xml_header($resp_charset);
605 if($this->debug > 0)
606 {
607 $payload = $payload . $this->serializeDebug($resp_charset);
608 }
609
610 // G. Giunta 2006-01-27: do not create response serialization if it has
611 // already happened. Helps building json magic
612 if (empty($r->payload))
613 {
614 $r->serialize($resp_charset);
615 }
616 $payload = $payload . $r->payload;
617
618 if ($return_payload)
619 {
620 return $payload;
621 }
622
623 // if we get a warning/error that has output some text before here, then we cannot
624 // add a new header. We cannot say we are sending xml, either...
625 if(!headers_sent())
626 {
627 header('Content-Type: '.$r->content_type);
628 // we do not know if client actually told us an accepted charset, but if he did
629 // we have to tell him what we did
630 header("Vary: Accept-Charset");
631
632 // http compression of output: only
633 // if we can do it, and we want to do it, and client asked us to,
634 // and php ini settings do not force it already
635 $php_no_self_compress = ini_get('zlib.output_compression') == '' && (ini_get('output_handler') != 'ob_gzhandler');
636 if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
637 && $php_no_self_compress)
638 {
639 if(strpos($resp_encoding, 'gzip') !== false)
640 {
641 $payload = gzencode($payload);
642 header("Content-Encoding: gzip");
643 header("Vary: Accept-Encoding");
644 }
645 elseif (strpos($resp_encoding, 'deflate') !== false)
646 {
647 $payload = gzcompress($payload);
648 header("Content-Encoding: deflate");
649 header("Vary: Accept-Encoding");
650 }
651 }
652
653 // do not ouput content-length header if php is compressing output for us:
654 // it will mess up measurements
655 if($php_no_self_compress)
656 {
657 header('Content-Length: ' . (int)strlen($payload));
658 }
659 }
660 else
661 {
662 error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages');
663 }
664
665 print $payload;
666
667 // return request, in case subclasses want it
668 return $r;
669 }
670
671 /**
672 * Add a method to the dispatch map
673 * @param string $methodname the name with which the method will be made available
674 * @param string $function the php function that will get invoked
675 * @param array $sig the array of valid method signatures
676 * @param string $doc method documentation
677 * @access public
678 */
679 function add_to_map($methodname,$function,$sig=null,$doc='')
680 {
681 $this->dmap[$methodname] = array(
682 'function' => $function,
683 'docstring' => $doc
684 );
685 if ($sig)
686 {
687 $this->dmap[$methodname]['signature'] = $sig;
688 }
689 }
690
691 /**
692 * Verify type and number of parameters received against a list of known signatures
693 * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
694 * @param array $sig array of known signatures to match against
695 * @access private
696 */
697 function verifySignature($in, $sig)
698 {
699 // check each possible signature in turn
700 if (is_object($in))
701 {
702 $numParams = $in->getNumParams();
703 }
704 else
705 {
706 $numParams = count($in);
707 }
708 foreach($sig as $cursig)
709 {
710 if(count($cursig)==$numParams+1)
711 {
712 $itsOK=1;
713 for($n=0; $n<$numParams; $n++)
714 {
715 if (is_object($in))
716 {
717 $p=$in->getParam($n);
718 if($p->kindOf() == 'scalar')
719 {
720 $pt=$p->scalartyp();
721 }
722 else
723 {
724 $pt=$p->kindOf();
725 }
726 }
727 else
728 {
729 $pt= $in[$n] == 'i4' ? 'int' : $in[$n]; // dispatch maps never use i4...
730 }
731
732 // param index is $n+1, as first member of sig is return type
733 if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
734 {
735 $itsOK=0;
736 $pno=$n+1;
737 $wanted=$cursig[$n+1];
738 $got=$pt;
739 break;
740 }
741 }
742 if($itsOK)
743 {
744 return array(1,'');
745 }
746 }
747 }
748 if(isset($wanted))
749 {
750 return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
751 }
752 else
753 {
754 return array(0, "No method signature matches number of parameters");
755 }
756 }
757
758 /**
759 * Parse http headers received along with xmlrpc request. If needed, inflate request
760 * @return null on success or an xmlrpcresp
761 * @access private
762 */
763 function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
764 {
765 // Play nice to PHP 4.0.x: superglobals were not yet invented...
766 if(!isset($_SERVER))
767 {
768 $_SERVER = $GLOBALS['HTTP_SERVER_VARS'];
769 }
770
771 if($this->debug > 1)
772 {
773 if(function_exists('getallheaders'))
774 {
775 $this->debugmsg(''); // empty line
776 foreach(getallheaders() as $name => $val)
777 {
778 $this->debugmsg("HEADER: $name: $val");
779 }
780 }
781
782 }
783
784 if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
785 {
786 $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
787 }
788 else
789 {
790 $content_encoding = '';
791 }
792
793 // check if request body has been compressed and decompress it
794 if($content_encoding != '' && strlen($data))
795 {
796 if($content_encoding == 'deflate' || $content_encoding == 'gzip')
797 {
798 // if decoding works, use it. else assume data wasn't gzencoded
799 if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
800 {
801 if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
802 {
803 $data = $degzdata;
804 if($this->debug > 1)
805 {
806 $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
807 }
808 }
809 elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
810 {
811 $data = $degzdata;
812 if($this->debug > 1)
813 $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
814 }
815 else
816 {
817 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
818 return $r;
819 }
820 }
821 else
822 {
823 //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
824 $r =& new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
825 return $r;
826 }
827 }
828 }
829
830 // check if client specified accepted charsets, and if we know how to fulfill
831 // the request
832 if ($this->response_charset_encoding == 'auto')
833 {
834 $resp_encoding = '';
835 if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
836 {
837 // here we should check if we can match the client-requested encoding
838 // with the encodings we know we can generate.
839 /// @todo we should parse q=0.x preferences instead of getting first charset specified...
840 $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
841 // Give preference to internal encoding
842 $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
843 foreach ($known_charsets as $charset)
844 {
845 foreach ($client_accepted_charsets as $accepted)
846 if (strpos($accepted, $charset) === 0)
847 {
848 $resp_encoding = $charset;
849 break;
850 }
851 if ($resp_encoding)
852 break;
853 }
854 }
855 }
856 else
857 {
858 $resp_encoding = $this->response_charset_encoding;
859 }
860
861 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
862 {
863 $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
864 }
865 else
866 {
867 $resp_compression = '';
868 }
869
870 // 'guestimate' request encoding
871 /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
872 $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
873 $data);
874
875 return null;
876 }
877
878 /**
879 * Parse an xml chunk containing an xmlrpc request and execute the corresponding
880 * php function registered with the server
881 * @param string $data the xml request
882 * @param string $req_encoding (optional) the charset encoding of the xml request
883 * @return xmlrpcresp
884 * @access private
885 */
886 function parseRequest($data, $req_encoding='')
887 {
888 // 2005/05/07 commented and moved into caller function code
889 //if($data=='')
890 //{
891 // $data=$GLOBALS['HTTP_RAW_POST_DATA'];
892 //}
893
894 // G. Giunta 2005/02/13: we do NOT expect to receive html entities
895 // so we do not try to convert them into xml character entities
896 //$data = xmlrpc_html_entity_xlate($data);
897
898 $GLOBALS['_xh']=array();
899 $GLOBALS['_xh']['ac']='';
900 $GLOBALS['_xh']['stack']=array();
901 $GLOBALS['_xh']['valuestack'] = array();
902 $GLOBALS['_xh']['params']=array();
903 $GLOBALS['_xh']['pt']=array();
904 $GLOBALS['_xh']['isf']=0;
905 $GLOBALS['_xh']['isf_reason']='';
906 $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
907 $GLOBALS['_xh']['rt']='';
908
909 // decompose incoming XML into request structure
910 if ($req_encoding != '')
911 {
912 if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
913 // the following code might be better for mb_string enabled installs, but
914 // makes the lib about 200% slower...
915 //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
916 {
917 error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding);
918 $req_encoding = $GLOBALS['xmlrpc_defencoding'];
919 }
920 /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
921 // the encoding is not UTF8 and there are non-ascii chars in the text...
922 /// @todo use an ampty string for php 5 ???
923 $parser = xml_parser_create($req_encoding);
924 }
925 else
926 {
927 $parser = xml_parser_create();
928 }
929
930 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
931 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
932 // the xml parser to give us back data in the expected charset
933 // What if internal encoding is not in one of the 3 allowed?
934 // we use the broadest one, ie. utf8
935 // This allows to send data which is native in various charset,
936 // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
937 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
938 {
939 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
940 }
941 else
942 {
943 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
944 }
945
946 if ($this->functions_parameters_type != 'xmlrpcvals')
947 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
948 else
949 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
950 xml_set_character_data_handler($parser, 'xmlrpc_cd');
951 xml_set_default_handler($parser, 'xmlrpc_dh');
952 if(!xml_parse($parser, $data, 1))
953 {
954 // return XML error as a faultCode
955 $r=&new xmlrpcresp(0,
956 $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
957 sprintf('XML error: %s at line %d, column %d',
958 xml_error_string(xml_get_error_code($parser)),
959 xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
960 xml_parser_free($parser);
961 }
962 elseif ($GLOBALS['_xh']['isf'])
963 {
964 xml_parser_free($parser);
965 $r=&new xmlrpcresp(0,
966 $GLOBALS['xmlrpcerr']['invalid_request'],
967 $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
968 }
969 else
970 {
971 xml_parser_free($parser);
972 if ($this->functions_parameters_type != 'xmlrpcvals')
973 {
974 if($this->debug > 1)
975 {
976 $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
977 }
978 $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
979 }
980 else
981 {
982 // build an xmlrpcmsg object with data parsed from xml
983 $m=&new xmlrpcmsg($GLOBALS['_xh']['method']);
984 // now add parameters in
985 for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
986 {
987 $m->addParam($GLOBALS['_xh']['params'][$i]);
988 }
989
990 if($this->debug > 1)
991 {
992 $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
993 }
994 $r = $this->execute($m);
995 }
996 }
997 return $r;
998 }
999
1000 /**
1001 * Execute a method invoked by the client, checking parameters used
1002 * @param mixed $m either an xmlrpcmsg obj or a method name
1003 * @param array $params array with method parameters as php types (if m is method name only)
1004 * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1005 * @return xmlrpcresp
1006 * @access private
1007 */
1008 function execute($m, $params=null, $paramtypes=null)
1009 {
1010 if (is_object($m))
1011 {
1012 $methName = $m->method();
1013 }
1014 else
1015 {
1016 $methName = $m;
1017 }
1018 $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1019 $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1020
1021 if(!isset($dmap[$methName]['function']))
1022 {
1023 // No such method
1024 return new xmlrpcresp(0,
1025 $GLOBALS['xmlrpcerr']['unknown_method'],
1026 $GLOBALS['xmlrpcstr']['unknown_method']);
1027 }
1028
1029 // Check signature
1030 if(isset($dmap[$methName]['signature']))
1031 {
1032 $sig = $dmap[$methName]['signature'];
1033 if (is_object($m))
1034 {
1035 list($ok, $errstr) = $this->verifySignature($m, $sig);
1036 }
1037 else
1038 {
1039 list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1040 }
1041 if(!$ok)
1042 {
1043 // Didn't match.
1044 return new xmlrpcresp(
1045 0,
1046 $GLOBALS['xmlrpcerr']['incorrect_params'],
1047 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1048 );
1049 }
1050 }
1051
1052 $func = $dmap[$methName]['function'];
1053 // let the 'class::function' syntax be accepted in dispatch maps
1054 if(is_string($func) && strpos($func, '::'))
1055 {
1056 $func = explode('::', $func);
1057 }
1058 // verify that function to be invoked is in fact callable
1059 if(!is_callable($func))
1060 {
1061 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable");
1062 return new xmlrpcresp(
1063 0,
1064 $GLOBALS['xmlrpcerr']['server_error'],
1065 $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1066 );
1067 }
1068
1069 // If debug level is 3, we should catch all errors generated during
1070 // processing of user function, and log them as part of response
1071 if($this->debug > 2)
1072 {
1073 $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1074 }
1075 if (is_object($m))
1076 {
1077 if($sysCall)
1078 {
1079 $r = call_user_func($func, $this, $m);
1080 }
1081 else
1082 {
1083 $r = call_user_func($func, $m);
1084 }
1085 if (!is_a($r, 'xmlrpcresp'))
1086 {
1087 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object");
1088 if (is_a($r, 'xmlrpcval'))
1089 {
1090 $r =& new xmlrpcresp($r);
1091 }
1092 else
1093 {
1094 $r =& new xmlrpcresp(
1095 0,
1096 $GLOBALS['xmlrpcerr']['server_error'],
1097 $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1098 );
1099 }
1100 }
1101 }
1102 else
1103 {
1104 // call a 'plain php' function
1105 if($sysCall)
1106 {
1107 array_unshift($params, $this);
1108 $r = call_user_func_array($func, $params);
1109 }
1110 else
1111 {
1112 // 3rd API convention for method-handling functions: EPI-style
1113 if ($this->functions_parameters_type == 'epivals')
1114 {
1115 $r = call_user_func_array($func, array($methName, $params, $this->user_data));
1116 // mimic EPI behaviour: if we get an array that looks like an error, make it
1117 // an eror response
1118 if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1119 {
1120 $r =& new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1121 }
1122 else
1123 {
1124 // functions using EPI api should NOT return resp objects,
1125 // so make sure we encode the return type correctly
1126 $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1127 }
1128 }
1129 else
1130 {
1131 $r = call_user_func_array($func, $params);
1132 }
1133 }
1134 // the return type can be either an xmlrpcresp object or a plain php value...
1135 if (!is_a($r, 'xmlrpcresp'))
1136 {
1137 // what should we assume here about automatic encoding of datetimes
1138 // and php classes instances???
1139 $r =& new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates')));
1140 }
1141 }
1142 if($this->debug > 2)
1143 {
1144 // note: restore the error handler we found before calling the
1145 // user func, even if it has been changed inside the func itself
1146 if($GLOBALS['_xmlrpcs_prev_ehandler'])
1147 {
1148 set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1149 }
1150 else
1151 {
1152 restore_error_handler();
1153 }
1154 }
1155 return $r;
1156 }
1157
1158 /**
1159 * add a string to the 'internal debug message' (separate from 'user debug message')
1160 * @param string $strings
1161 * @access private
1162 */
1163 function debugmsg($string)
1164 {
1165 $this->debug_info .= $string."\n";
1166 }
1167
1168 /**
1169 * @access private
1170 */
1171 function xml_header($charset_encoding='')
1172 {
1173 if ($charset_encoding != '')
1174 {
1175 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1176 }
1177 else
1178 {
1179 return "<?xml version=\"1.0\"?" . ">\n";
1180 }
1181 }
1182
1183 /**
1184 * A debugging routine: just echoes back the input packet as a string value
1185 * DEPRECATED!
1186 */
1187 function echoInput()
1188 {
1189 $r=&new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1190 print $r->serialize();
1191 }
1192 }
1193?> \ No newline at end of file