summary refs log tree commit diff stats
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/bbcode.php151
-rw-r--r--includes/comments.php97
-rwxr-xr-xincludes/db.php49
-rw-r--r--includes/definitions.php52
-rw-r--r--includes/footer.php198
-rw-r--r--includes/functions.php244
-rw-r--r--includes/functions_quotes.php195
-rw-r--r--includes/header.php66
-rw-r--r--includes/hits.php102
-rw-r--r--includes/http.php1982
-rw-r--r--includes/index.php5
-rw-r--r--includes/phpsvnclient.php420
-rw-r--r--includes/recaptchalib.php277
-rw-r--r--includes/session.php61
-rwxr-xr-xincludes/specialdates.php314
-rw-r--r--includes/template.php164
-rw-r--r--includes/updatePending.php51
-rw-r--r--includes/xml_parser.php427
-rw-r--r--includes/xmlrpc/array_key_exists.php55
-rw-r--r--includes/xmlrpc/is_a.php47
-rw-r--r--includes/xmlrpc/is_callable.php53
-rw-r--r--includes/xmlrpc/is_scalar.php38
-rw-r--r--includes/xmlrpc/var_export.php105
-rw-r--r--includes/xmlrpc/version_compare.php179
-rw-r--r--includes/xmlrpc/xmlrpc.inc3705
-rw-r--r--includes/xmlrpc/xmlrpc_wrappers.inc819
-rw-r--r--includes/xmlrpc/xmlrpcs.inc1193
27 files changed, 11049 insertions, 0 deletions
diff --git a/includes/bbcode.php b/includes/bbcode.php new file mode 100644 index 0000000..4a1870a --- /dev/null +++ b/includes/bbcode.php
@@ -0,0 +1,151 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/bbcode.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25$bbcode = bbcode_create(array('' => array('type' => BBCODE_TYPE_ROOT)));
26
27// [b][/b] - Bold
28bbcode_add_element($bbcode,'b',array( 'type' => BBCODE_TYPE_NOARG,
29 'open_tag' => '<B>',
30 'close_tag' => '</B>'));
31
32// [i][/i] - Italic
33bbcode_add_element($bbcode,'i',array( 'type' => BBCODE_TYPE_NOARG,
34 'open_tag' => '<I>',
35 'close_tag' => '</I>'));
36
37// [url][/url] - [url=][/url] - Link
38bbcode_add_element($bbcode,'url',array( 'type' => BBCODE_TYPE_OPTARG,
39 'open_tag' => '<a href="{PARAM}">',
40 'close_tag' => '</a>',
41 'default_arg' => '{CONTENT}'));
42
43// [img][/img] - [img=][/img] - Image
44bbcode_add_element($bbcode,'img',array( 'type' => BBCODE_TYPE_OPTARG,
45 'open_tag' => '<IMG SRC="',
46 'close_tag' => '" ALT="{PARAM}" ALIGN="RIGHT"></IMG>',
47 'default_tag' => '{CONTENT}'));
48
49// [big][/big] - Big
50bbcode_add_element($bbcode,'big',array( 'type' => BBCODE_TYPE_NOARG,
51 'open_tag' => '<BIG>',
52 'close_tag' => '</BIG>'));
53
54// [small][/small] - Small
55bbcode_add_element($bbcode,'small',array( 'type' => BBCODE_TYPE_NOARG,
56 'open_tag' => '<SMALL>',
57 'close_tag' => '</SMALL>'));
58
59// [ul][/ul] - Unordered List
60bbcode_add_element($bbcode,'ul',array( 'type' => BBCODE_TYPE_NOARG,
61 'open_tag' => '<UL>',
62 'close_tag' => '</UL>',
63 'childs' => 'li'));
64
65// [ol][/ol] - Ordered List
66bbcode_add_element($bbcode,'ol',array( 'type' => BBCODE_TYPE_NOARG,
67 'open_tag' => '<OL>',
68 'close_tag' => '</OL>',
69 'childs' => 'li'));
70
71// [li][/li] - List Item
72bbcode_add_element($bbcode,'li',array( 'type' => BBCODE_TYPE_NOARG,
73 'open_tag' => '<LI>',
74 'close_tag' => '</LI>'));
75
76// [code][/code] - Code
77bbcode_add_element($bbcode,'code',array( 'type' => BBCODE_TYPE_NOARG,
78 'open_tag' => '<CODE>',
79 'close_tag' => '</CODE>'));
80
81// [pre][/pre] - Preformatted Code
82bbcode_add_element($bbcode,'pre',array( 'type' => BBCODE_TYPE_NOARG,
83 'open_tag' => '<P><DIV CLASS="autosize"><DIV CLASS="bubble"><DIV CLASS="bquote"><BLOCKQUOTE><DIV><PRE>',
84 'close_tag' => '</PRE></DIV></BLOCKQUOTE></DIV></DIV></DIV><DIV CLASS="cleardiv"></DIV>'));
85
86function bb_fixCode($string)
87{
88 $he = htmlentities($string);
89 $br = nl2br($he);
90 $sp = str_replace(' ','&nbsp;$nbsp;',$br);
91 $ta = str_replace(' ','&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',$sp);
92 return($ta);
93}
94
95// [blog][/blog] - Blog Link
96bbcode_add_element($bbcode,'blog',array( 'type' => BBCODE_TYPE_OPTARG,
97 'open_tag' => (isset($oldBlog) ? '<A HREF="/archives/{CONTENT}.php">' : '<A HREF="/blog/{PARAM}/">'),
98 'close_tag' => '</A>',
99 'default_arg' => '{CONTENT}'));
100
101// [quote][/quote] - Quotes DB Link
102bbcode_add_element($bbcode,'quote',array( 'type' => BBCODE_TYPE_NOARG,
103 'open_tag' => (isset($oldBlog) ? '<A HREF="http://quotes.fourisland.com/?{CONTENT}">#' : '<A HREF="/quotes/{CONTENT}.php">#'),
104 'close_tag' => '</A>'));
105
106// [ins][/ins] - Insert
107bbcode_add_element($bbcode,'ins',array( 'type' => BBCODE_TYPE_NOARG,
108 'open_tag' => '<INS>',
109 'close_tag' => '</INS>'));
110
111// [del][/del] - Delete
112bbcode_add_element($bbcode,'del',array( 'type' => BBCODE_TYPE_NOARG,
113 'open_tag' => '<DEL>',
114 'close_tag' => '</DEL>'));
115
116// [bquote][/bquote] - Blockquote
117bbcode_add_element($bbcode,'bquote',array( 'type' => BBCODE_TYPE_OPTARG,
118 'open_tag' => '<P><DIV CLASS="autosize"><DIV CLASS="bubble"><DIV CLASS="bquote"><BLOCKQUOTE><DIV><NOBR>',
119 'close_tag' => '</NOBR></DIV></BLOCKQUOTE></DIV><CITE><STRONG>{PARAM}</STRONG></CITE></DIV></DIV><DIV CLASS="cleardiv"></DIV>',
120 'default_arg' => 'Anonymous'));
121
122// [project][/project] - Project Link
123bbcode_add_element($bbcode,'project',array( 'type' => BBCODE_TYPE_NOARG,
124 'open_tag' => (isset($oldBlog) ? '<A HREF="http://projects.fourisland.com/{CONTENT}/">' : '<A HREF="/projects/{CONTENT}/">'),
125 'close_tag' => '</A>'));
126
127// [abbr][/abbr] - Abbreviation
128bbcode_add_element($bbcode,'abbr',array( 'type' => BBCODE_TYPE_OPTARG,
129 'open_tag' => '<ABBR TITLE="{PARAM}">',
130 'close_tag' => '</ABBR>',
131 'default_arg' => ''));
132
133// [br] - Line Break
134bbcode_add_element($bbcode,'br',array( 'type' => BBCODE_TYPE_SINGLE,
135 'open_tag' => '<BR>'));
136
137// [hidden][/hidden] - Hidden Text
138bbcode_add_element($bbcode,'hidden',array( 'type' => BBCODE_TYPE_OPTARG,
139 'open_tag' => '<DIV STYLE="display: none">',
140 'close_tag' => '</DIV>',
141 'default_arg' => ''));
142
143function parseBBCode($text)
144{
145 global $bbcode;
146 $to_parse = str_replace("\n",'[br]',htmlentities($text));
147 $to_parse = bbcode_parse($bbcode,$to_parse);
148 return str_replace('[br]','',$to_parse);
149}
150
151?>
diff --git a/includes/comments.php b/includes/comments.php new file mode 100644 index 0000000..9cb8b18 --- /dev/null +++ b/includes/comments.php
@@ -0,0 +1,97 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/comments.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25include('includes/recaptchalib.php');
26$publickey = "6LfgvgEAAAAAAG_BJMkWk8sNcT1nBaGoXKJYb-JT";
27$privatekey = "6LfgvgEAAAAAAD0_UVLp57MU7tqcypsbZPS9qTnr";
28
29$curID = 0;
30
31$template = new FITemplate('comments');
32$template->add('PAGEID',$page_id);
33$template->add('CODEDDEF',urlencode('http://www.fourisland.com/images/error404.png'));
34$template->add('USERNAME',(isLoggedIn() ? sess_get('uname') : 'Anonymous'));
35
36if (!isLoggedIn())
37{
38 $template->add('RECAPTCHA',recaptcha_get_html($publickey));
39 $template->adds_block('NOLOG',array('exi'=>1));
40}
41
42$getcomments = "SELECT * FROM comments WHERE page_id = \"" . $page_id . "\" ORDER BY posttime";
43$getcomments2 = mysql_query($getcomments) or die($getcomments);
44$i=0;
45while ($getcomments3[$i] = mysql_fetch_array($getcomments2))
46{
47 $getuser = "SELECT * FROM users WHERE username = \"" . $getcomments3[$i]['username'] . "\"";
48 $getuser2 = mysql_query($getuser);
49 $getuser3 = mysql_fetch_array($getuser2);
50
51 if ($getuser3['username'] == $getcomments3[$i]['username'])
52 {
53 $username = $getuser3['username'];
54 $email = $getuser3['user_email'];
55 $website = $getuser3['website'];
56 } else {
57 $getanon = "SELECT * FROM anon_commenters WHERE username = \"" . $getcomments3[$i]['username'] . "\"";
58 $getanon2 = mysql_query($getanon);
59 $getanon3 = mysql_fetch_array($getanon2);
60
61 if ($getanon3['username'] == $getcomments3[$i]['username'])
62 {
63 $username = $getanon3['username'] . ' (Guest)';
64 $email = $getanon3['email'];
65 $website = $getanon3['website'];
66 }
67 }
68
69 if (isset($username))
70 {
71 if ($getcomments3[$i]['rating'] > -2)
72 {
73 $text = parseBBCode($getcomments3[$i]['comment']);
74 } else {
75 $text = 'This comment has been rated down below the threshold for public viewing (-1), suggesting that it may contain inappropriate or off topic content. (Or it may have been flame bait, or simply bad!)';
76 }
77
78 if ($getcomments3[$i]['title'] != '')
79 {
80 $title2 = $getcomments3[$i]['title'];
81 } else {
82 $title2 = 'Untitled';
83 }
84
85 $template->add_ref($curID, 'COMMENTS', array( 'CODEDEMAIL' => md5(strtolower($email)),
86 'USERNAME' => (($website != '') ? '<A HREF="http://' . $website . '">' . $username . '</A>' : $username),
87 'DATE' => date("F dS Y \a\\t g:i:s a",strtotime($getcomments3[$i]['posttime'])),
88 'RATING' => $getcomments3[$i]['rating'],
89 'ID' => $getcomments3[$i]['id'],
90 'TEXT' => $text,
91 'TITLE' => $title2));
92 }
93 $i++;
94}
95$template->display();
96
97?>
diff --git a/includes/db.php b/includes/db.php new file mode 100755 index 0000000..8cc5449 --- /dev/null +++ b/includes/db.php
@@ -0,0 +1,49 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/db.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25// Make the database connection.
26mysql_connect($dbwebhost, $dbwebuser, $dbwebpasswd);
27mysql_select_db($dbwebname);
28
29function db_single_select($query)
30{
31 $getitem1 = $query;
32 $getitem2 = mysql_query($getitem1) or die($getitem1);
33 $getitem3 = mysql_fetch_array($getitem2);
34 return $getitem3;
35}
36
37function db_multi_select($query, $callback)
38{
39 $getitem1 = $query;
40 $getitem2 = mysql_query($getitem1) or die($getitem1);
41 $i=0;
42 while ($getitem3[$i] = mysql_fetch_array($getitem2))
43 {
44 $callback($getitem3[$i]);
45 $i++;
46 }
47}
48
49?>
diff --git a/includes/definitions.php b/includes/definitions.php new file mode 100644 index 0000000..581eb85 --- /dev/null +++ b/includes/definitions.php
@@ -0,0 +1,52 @@
1<?php
2/*
3***************************************************************************
4* Copyright (C) 2007-2008 by Sixdegrees *
5* cesar@sixdegrees.com.br *
6* "Working with freedom" *
7* http://www.sixdegrees.com.br *
8* *
9* Permission is hereby granted, free of charge, to any person obtaining *
10* a copy of this software and associated documentation files (the *
11* "Software"), to deal in the Software without restriction, including *
12* without limitation the rights to use, copy, modify, merge, publish, *
13* distribute, sublicense, and/or sell copies of the Software, and to *
14* permit persons to whom the Software is furnished to do so, subject to *
15* the following conditions: *
16* *
17* The above copyright notice and this permission notice shall be *
18* included in all copies or substantial portions of the Software. *
19* *
20* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
21* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
22* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
23* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
24* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26* OTHER DEALINGS IN THE SOFTWARE. *
27***************************************************************************
28*/
29define("PHPSVN_NORMAL_REQUEST",'<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"><prop>
30<getlastmodified xmlns="DAV:"/><version-controlled-configuration xmlns="DAV:"/><resourcetype xmlns="DAV:"/><baseline-relative-path xmlns="http://subversion.tigris.org/xmlns/dav/"/><repository-uuid xmlns="http://subversion.tigris.org/xmlns/dav/"/></prop></propfind>');
31define("PHPSVN_VERSION_REQUEST",'<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"><prop><checked-in xmlns="DAV:"/></prop></propfind>');
32define("PHPSVN_LOGS_REQUEST",'<?xml version="1.0" encoding="utf-8"?> <S:log-report xmlns:S="svn:"> <S:start-revision>%d</S:start-revision><S:end-revision>%d</S:end-revision><S:path></S:path></S:log-report>');
33
34define("SVN_LAST_MODIFIED","lp1:getlastmodified");
35define("SVN_URL","D:href");
36define("SVN_RELATIVE_URL","lp3:baseline-relative-path");
37define("SVN_FILE_ID","lp3:repository-uuid");
38define("SVN_STATUS","D:status");
39define("SVN_IN_FILE","D:propstat");
40define("SVN_FILE","D:response");
41
42define("SVN_LOGS_BEGINGS","S:log-item");
43define("SVN_LOGS_VERSION","D:version-name");
44define("SVN_LOGS_AUTHOR","D:creator-displayname");
45define("SVN_LOGS_DATE","S:date");
46define("SVN_LOGS_COMMENT","D:comment");
47
48define("NOT_FOUND", 2);
49define("AUTH_REQUIRED", 3);
50define("UNKNOWN_ERROR",4);
51define("NO_ERROR",1)
52?>
diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..e4c88c8 --- /dev/null +++ b/includes/footer.php
@@ -0,0 +1,198 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/footer.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25$template = new FITemplate('footer');
26
27$template->add('EXTRASIDEBARS',(isset($extraSidebars) ? $extraSidebars : ''));
28
29if (isset($onFourm))
30{
31 $template->adds_block('ONFOURM',array('exi'=>1));
32}
33
34if (!isset($noRightbar))
35{
36 $template->adds_block('RIGHTBAR',array('exi'=>1));
37
38 if (!isset($noHatNav))
39 {
40 $template->adds_block('USEHATNAV', array('exi'=>1));
41 if (!isset($genHatNav))
42 {
43 $gethnis = 'SELECT * FROM hatnav WHERE category = "' .
44 $pageCategory . '"';
45 $gethnis2 = mysql_query($gethnis);
46 $i=0;
47 while ($gethnis3[$i] = mysql_fetch_array($gethnis2))
48 {
49 $template->adds_block('HATNAV', array( 'AID' => $gethnis3[$i]['AID'],
50 'HREF' => $gethnis3[$i]['href'],
51 'IMAGE' => '/theme/images/icons/' . $gethnis3[$i]['image'] . '.png',
52 'TEXT' => $gethnis3[$i]['text'],
53 'NEW' => dispIfNotOld($gethnis3[$i]['lastEdit'])));
54 $i++;
55 }
56 } else {
57 $i=0;
58 while ($i < $genHatNavNum)
59 {
60 $template->adds_block('HATNAV', array( 'AID' => 'post',
61 'HREF' => $genHatNav[$i]['href'],
62 'IMAGE' => '/theme/images/blue.PNG',
63 'TEXT' => $genHatNav[$i]['text'],
64 'NEW' => ''));
65 $i++;
66 }
67 }
68 }
69
70 include('pages/polloftheweek.php');
71
72 $getpopular = "SELECT * FROM updates ORDER BY popularity DESC LIMIT 0,5";
73 $getpopular2 = mysql_query($getpopular);
74 $i=0;
75 while ($getpopular3[$i] = mysql_fetch_array($getpopular2))
76 {
77 $template->adds_block('POPULAR', array( 'CODED' => $getpopular3[$i]['slug'],
78 'TITLE' => stripslashes($getpopular3[$i]['title'])));
79 $i++;
80 }
81
82 $getcomments = "SELECT * FROM comments WHERE page_id LIKE \"updates-%\" ORDER BY id DESC LIMIT 0,5";
83 $getcomments2 = mysql_query($getcomments);
84 $i=0;
85 while ($getcomments3[$i] = mysql_fetch_array($getcomments2))
86 {
87 $getpost = "SELECT * FROM updates WHERE id = " . substr($getcomments3[$i]['page_id'],strpos($getcomments3[$i]['page_id'],'-')+1);
88 $getpost2 = mysql_query($getpost);
89 $getpost3 = mysql_fetch_array($getpost2);
90
91 $getuser = "SELECT * FROM users WHERE username = \"" . $getcomments3[$i]['username'] . "\"";
92 $getuser2 = mysql_query($getuser);
93 $getuser3 = mysql_fetch_array($getuser2);
94
95 if ($getuser3['username'] == $getcomments3[$i]['username'])
96 {
97 $username = $getuser3['username'];
98 $website = $getuser3['website'];
99 } else {
100 $getanon = "SELECT * FROM anon_commenters WHERE username = \"" . $getcomments3[$i]['username'] . "\"";
101 $getanon2 = mysql_query($getanon);
102 $getanon3 = mysql_fetch_array($getanon2);
103
104 if ($getanon3['username'] == $getcomments3[$i]['username'])
105 {
106 $username = $getanon3['username'] . ' (Guest)';
107 $website = $getanon3['website'];
108 }
109 }
110
111
112 $template->adds_block('COMMENTS', array( 'CODED' => $getpost3['slug'],
113 'TITLE' => stripslashes($getpost3['title']),
114 'AUTHOR' => (($website != '') ? '<A HREF="http://' . $website . '">' . $username . '</A>' : $username)));
115 $i++;
116 }
117
118 $getusers = "SELECT DISTINCT username FROM comments";
119 $getusers2 = mysql_query($getusers);
120 $i=0;
121 while ($getusers3[$i] = mysql_fetch_array($getusers2))
122 {
123 $getcount = "SELECT COUNT(*) FROM comments WHERE username = \"" . $getusers3[$i]['username'] . "\"";
124 $getcount2 = mysql_query($getcount);
125 $getcount3 = mysql_fetch_array($getcount2);
126
127 $getuser = "SELECT * FROM users WHERE username = \"" . $getusers3[$i]['username'] . "\"";
128 $getuser2 = mysql_query($getuser);
129 $getuser3 = mysql_fetch_array($getuser2);
130
131 if ($getuser3['username'] == $getusers3[$i]['username'])
132 {
133 $username = $getuser3['username'];
134 $website = $getuser3['website'];
135 } else {
136 $getanon = "SELECT * FROM anon_commenters WHERE username = \"" . $getusers3[$i]['username'] . "\"";
137 $getanon2 = mysql_query($getanon);
138 $getanon3 = mysql_fetch_array($getanon2);
139
140 if ($getanon3['username'] == $getusers3[$i]['username'])
141 {
142 $username = $getanon3['username'] . ' (Guest)';
143 $website = $getanon3['website'];
144 }
145 }
146
147 $name = (($website != '') ? '<A HREF="http://' . $website . '">' . $username . '</A>' : $username);
148 $users[$name] = $getcount3[0];
149
150 $i++;
151 }
152
153 arsort($users);
154 $i=0;
155 foreach ($users as $name => $count)
156 {
157 if ($i == 5)
158 {
159 break;
160 }
161
162 if ($name != 'Pingback')
163 {
164 $template->adds_block('TOP', array( 'USERNAME' => $name,
165 'COUNT' => $count));
166 $i++;
167 }
168 }
169
170 $gethits = "SELECT * FROM config WHERE name = \"hits\"";
171 $gethits2 = mysql_query($gethits);
172 $gethits3 = mysql_fetch_array($gethits2);
173 $template->add('HITS', $gethits3['value']);
174
175 $gethits = "SELECT * FROM config WHERE name = \"todayHits\"";
176 $gethits2 = mysql_query($gethits);
177 $gethits3 = mysql_fetch_array($gethits2);
178 $template->add('TODAY', $gethits3['value']);
179
180 $getpost = "SELECT * FROM phpbb_posts ORDER BY post_id DESC LIMIT 0,5";
181 $getpost2 = mysql_query($getpost) or die($getpost);
182 $i=0;
183 while ($getpost3[$i] = mysql_fetch_array($getpost2))
184 {
185 $getuser = "SELECT * FROM phpbb_users WHERE user_id = " . $getpost3[$i]['poster_id'];
186 $getuser2 = mysql_query($getuser) or die($getuser);
187 $getuser3 = mysql_fetch_array($getuser2);
188
189 $template->adds_block('FOURM', array( 'SUBJECT' => $getpost3[$i]['post_subject'],
190 'TOPIC' => $getpost3[$i]['topic_id'],
191 'POST' => $getpost3[$i]['post_id'],
192 'USERNAME' => $getuser3['username']));
193 }
194}
195
196$template->display();
197
198?>
diff --git a/includes/functions.php b/includes/functions.php new file mode 100644 index 0000000..c377c29 --- /dev/null +++ b/includes/functions.php
@@ -0,0 +1,244 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/functions.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25function isLoggedIn()
26{
27 return sess_exists('uname');
28}
29
30function getUserlevel()
31{
32 if (isLoggedIn())
33 {
34 $getuser = "SELECT * FROM users WHERE username = \"" . sess_get('uname') . "\"";
35 $getuser2 = mysql_query($getuser);
36 $getuser3 = mysql_fetch_array($getuser2);
37 return $getuser3['user_group'];
38 } else {
39 return 4;
40 }
41}
42
43function countRows($table, $extra = '')
44{
45 $cntrows = "SELECT * FROM " . $table . " " . $extra;
46 $cntrows2 = mysql_query($cntrows);
47 $i=0;
48 while ($cntrows3[$i] = mysql_fetch_array($cntrows2))
49 {
50 $i++;
51 }
52 return $i;
53}
54
55function generateError($error)
56{
57 ob_end_clean();
58 ob_start();
59 $errorid = $error;
60 include('pages/error.php');
61}
62
63function echoLogData()
64{
65 if (!isLoggedIn()) {
66 return('Login');
67 } else {
68 return('Logout');
69 }
70}
71
72function dispIfNotOld($datTim)
73{
74 if (($datTim+604800) >= time())
75 {
76 return('<IMG SRC="/theme/images/icons/new.png" ALT="New!" BORDER="0" WIDTH=16 HEIGHT=16 STYLE="vertical-align: middle;">');
77 } else {
78 return;
79 }
80}
81
82function getpercent($getpoll3,$num)
83{
84 $maxper = ($getpoll3['clicks1'] + $getpoll3['clicks2'] + $getpoll3['clicks3'] + $getpoll3['clicks4']);
85 $percent = round(($getpoll3['clicks' . $num] / $maxper) * 100);
86 return($percent);
87}
88
89function generateSlug($title,$table)
90{
91 $title = preg_replace('/[^A-Za-z0-9]/','-',$title);
92 $title = preg_replace('/-{2,}/','-',$title);
93 if (substr($title,0,1) == '-')
94 {
95 $title = substr($title,1);
96 }
97 if (substr($title,strlen($title)-1,1) == '-')
98 {
99 $title = substr($title,0,strlen($title)-1);
100 }
101 $title = strtolower($title);
102
103 $getprevs = "SELECT COUNT(*) FROM " . $table . " WHERE slug = \"" . $title . "\" OR slug LIKE \"" . $title . "-%\"";
104 $getprevs2 = mysql_query($getprevs);
105 @$getprevs3 = mysql_fetch_array($getprevs2);
106 if ($getprevs3[0] > 0)
107 {
108 $title .= '-' . ($getprevs3[0]+1);
109 }
110
111 return($title);
112}
113
114function postBlogPost($title,$author,$tag1,$tag2,$tag3,$content)
115{
116 $slug = generateSlug($title,'updates');
117
118 $inspost = "INSERT INTO updates (title,slug,author,tag1,tag2,tag3,text) VALUES (\"" . $title . "\",\"" . $slug . "\",\"" . $author . "\",\"" . $tag1 . "\",\"" . $tag2 . "\",\"" . $tag3 . "\",\"" . addslashes($content) . "\")";
119 $inspost2 = mysql_query($inspost);
120
121 $upconf = "UPDATE config SET value = \"" . date('md') . "\" WHERE name = \"lastUpdate\"";
122 $upconf2 = mysql_query($upconf);
123
124 preg_match_all('|<a\s[^>]*href="([^"]+)"|i', parseBBCode($content), $matches);
125
126 foreach ($matches[1] as $link)
127 {
128 if ($all_links[$link])
129 {
130 continue;
131 }
132
133 $all_links[$link] = true;
134
135 if (preg_match('/^https{0,1}:/i', $link))
136 {
137 $c = curl_init();
138 curl_setopt($c, CURLOPT_URL, $link);
139 curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
140 curl_setopt($c, CURLOPT_HEADER, false);
141 $page_data = curl_exec($c);
142 curl_close($c);
143
144 if (preg_match('/<LINK REL="pingback" HREF="([^"]+)"/i',$page_data,$server))
145 {
146 $client = new xmlrpc_client($server[1]);
147 $msg = new xmlrpcmsg("pingback.ping", array( new xmlrpcval('http://www.fourisland.com/blog/' . $slug . '/', 'string'),
148 new xmlrpcval($link, 'string')));
149 $client->send($msg);
150 }
151 }
152 }
153
154 $client = new xmlrpc_client('http://rpc.pingomatic.com');
155 $msg = new xmlrpcmsg("weblogUpdates.ping", array( new xmlrpcval('Four Island', 'string'),
156 new xmlrpcval('http://www.fourisland.com/', 'string')));
157 $client->send($msg);
158}
159
160function recalcPop($id)
161{
162 $getpost = "SELECT * FROM updates WHERE id = " . $id;
163 $getpost2 = mysql_query($getpost);
164 $getpost3 = mysql_fetch_array($getpost2);
165 $popularity = $getpost3['home_views'];
166 $popularity += ($getpost3['views']*2);
167 $popularity += ($getpost3['rating']*5);
168
169 $getcomments = "SELECT COUNT(*) FROM comments WHERE page_id = \"updates-" . $id . "\" AND username <> \"Pingback\"";
170 $getcomments2 = mysql_query($getcomments);
171 $getcomments3 = mysql_fetch_array($getcomments2);
172 $popularity += ($getcomments3[0] * 10);
173
174 $getpings = "SELECT COUNT(*) FROM comments WHERE page_id = \"updates-" . $id . "\" AND username = \"Pingback\"";
175 $getpings2 = mysql_query($getpings);
176 $getpings3 = mysql_fetch_array($getpings2);
177 $popularity += ($getpings3[0] * 25);
178
179 $setpost = "UPDATE updates SET popularity = " . $popularity . " WHERE id = " . $id;
180 $setpost2 = mysql_query($setpost);
181}
182
183function updatePop($id, $area, $plus=1)
184{
185 $gettrack = "SELECT * FROM tracking WHERE ip = \"" . $_SERVER['REMOTE_ADDR'] . "\"";
186 $gettrack2 = mysql_query($gettrack);
187 $gettrack3 = mysql_fetch_array($gettrack2);
188
189 $trackArr = explode(',',$gettrack3[$area]);
190
191 if (($gettrack3['ip'] != $_SERVER['REMOTE_ADDR']) || (array_search($id,$trackArr) === FALSE))
192 {
193 $setpost = "UPDATE updates SET " . $area . " = " . $area . "+" . $plus . " WHERE id = " . $id;
194 $setpost2 = mysql_query($setpost);
195 recalcPop($id);
196
197 if ($gettrack3['ip'] == $_SERVER['REMOTE_ADDR'])
198 {
199 $settrack = "UPDATE tracking SET " . $area . " = \"" . $gettrack3[$area] . "," . $id . "\" WHERE id = " . $gettrack3['id'];
200 } else {
201 $settrack = "INSERT INTO tracking (ip," . $area . ") VALUES (\"" . $_SERVER['REMOTE_ADDR'] . "\",\"" . $id . "\")";
202 }
203 $settrack2 = mysql_query($settrack) or die($settrack);
204 return 1;
205 } else {
206 return 0;
207 }
208}
209
210function verifyUser($username, $password)
211{
212 $getuser = 'SELECT * FROM users WHERE username = "' . $username . '" AND user_password = "' . md5($password) . '"';
213 $getuser2 = mysql_query($getuser);
214 $getuser3 = mysql_fetch_array($getuser2);
215 return (($_POST['username'] != '') && ($getuser3['username'] == $_POST['username']));
216}
217
218function updateCommentPop($id, $plus=1)
219{
220 $gettrack = "SELECT * FROM tracking_comments WHERE ip = \"" . $_SERVER['REMOTE_ADDR'] . "\"";
221 $gettrack2 = mysql_query($gettrack);
222 $gettrack3 = mysql_fetch_array($gettrack2);
223
224 $trackArr = explode(',',$gettrack3['comment_id']);
225
226 if (($gettrack3['ip'] != $_SERVER['REMOTE_ADDR']) || (array_search($id,$trackArr) === FALSE))
227 {
228 $setcomment = "UPDATE comments SET rating = rating+" . $plus . " WHERE id = " . $id;
229 $setcomment2 = mysql_query($setcomment);
230
231 if ($gettrack3['ip'] == $_SERVER['REMOTE_ADDR'])
232 {
233 $settrack = "UPDATE tracking SET rating = \"" . $gettrack3['comment_id'] . "," . $id . "\" WHERE id = " . $gettrack3['id'];
234 } else {
235 $settrack = "INSERT INTO tracking (ip,rating) VALUES (\"" . $_SERVER['REMOTE_ADDR'] . "\",\"" . $id . "\")";
236 }
237 $settrack2 = mysql_query($settrack) or die($settrack);
238 return 1;
239 } else {
240 return 0;
241 }
242}
243
244?>
diff --git a/includes/functions_quotes.php b/includes/functions_quotes.php new file mode 100644 index 0000000..e3ca2c0 --- /dev/null +++ b/includes/functions_quotes.php
@@ -0,0 +1,195 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/functions_quotes.inc
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25function quote_generation($query, $origin, $page = 1, $quote_limit = 50, $page_limit = 10)
26{
27 $template = new FITemplate('quotes/browse');
28 if ($page != -1)
29 {
30 $template->adds_block('PAGENUMBERS',array('exi'=>1));
31 page_numbers($template, $origin, $quote_limit, $page, $page_limit);
32 $up_lim = ($quote_limit * $page);
33 $low_lim = $up_lim - $quote_limit;
34 $query .= "LIMIT $low_lim,$quote_limit";
35 }
36 $template->add('ORIGIN',$origin);
37
38 $getquotes2 = mysql_query($query);
39 $i=0;
40 while ($getquotes3[$i] = mysql_fetch_array($getquotes2))
41 {
42 $template->adds_block('QUOTES',array( 'NUMBER' => $getquotes3[$i]['id'],
43 'RATING' => $getquotes3[$i]['rating'],
44 'QUOTE' => nl2br(stripslashes($getquotes3[$i]['quote']))));
45
46 $i++;
47 }
48
49 $template->display();
50}
51
52function page_numbers($template, $origin, $quote_limit, $page_default, $page_limit)
53{
54 $numrows = countRows('rash_quotes');
55 $testrows = $numrows;
56 $pagenum = (($testrows + 1) / ($quote_limit > 0 ? $quote_limit : 1));
57
58 if (($page_limit % 2))
59 {
60 $page_limit++;
61 }
62 if (($page_limit < 2) || (!$page_limit))
63 {
64 $page_limit = 5;
65 }
66
67 $pagebase = 0;
68 do
69 {
70 $pagebase++;
71 $page_limit -= 2;
72 } while ($page_limit > 1);
73
74 $template->add('LORIGIN',strtolower($origin));
75 $template->add('MINUSTEN',(($page_default - 10) > 1) ? ($page_default - 10) : 1);
76
77 if ($page_default - $pagebase > 1)
78 {
79 $template->add('BDDD','...');
80 }
81
82 $i = $page_default - $pagebase;
83 do
84 {
85 if ($i > 0)
86 {
87 $template->adds_block('BPAGES',array('PAGENUM' => $i));
88 }
89 $i++;
90 } while ($i < $page_default);
91
92 $template->add('CURPAGE',$page_default);
93
94 $i = $page_default + 1;
95 do
96 {
97 if ($i <= $pagenum)
98 {
99 $template->adds_block('APAGES',array('PAGENUM' => $i));
100 }
101 $i++;
102 } while ($i < ($page_default + $pagebase + 1));
103
104 if (($page_default + $pagebase) < $pagenum)
105 {
106 $template->add('ADDD','...');
107 }
108
109 $template->add('PLUSTEN',(($page_default + 10) < $pagenum) ? ($page_default + 10) : $pagenum);
110 $template->add('LASTPAGE',$pagenum);
111}
112
113function user_quote_status($where, $quote_num, $template)
114{
115 $tracking_verdict = ip_track($where, $quote_num);
116 if ($where != 'flag')
117 {
118 switch ($tracking_verdict)
119 {
120 case 1:
121 $template->add('TRACKING',"Quote has been modified, and data of your action has been recorded in the database.");
122 break;
123 case 2:
124 $template->add('TRACKING',"Quote has been modified, your IP has been logged, and data of your action has been recorded in the database.");
125 break;
126 case 3:
127 $template->add('TRACKING',"You have already voted on this quote, please try again later.");
128 break;
129 }
130 }
131 return $tracking_verdict;
132}
133
134function ip_track($where, $quote_num)
135{
136 switch ($where)
137 {
138 case 'flag':
139 $where2 = 'vote';
140 break;
141 case 'vote':
142 $where2 = 'flag';
143 break;
144 }
145
146 $getip = "SELECT * FROM rash_tracking WHERE ip = \"" . $_SERVER['REMOTE_ADDR'] . "\"";
147 $getip2 = mysql_query($getip);
148 $getip3 = mysql_fetch_array($getip2);
149
150 if ($getip3['ip'] == $_SERVER['REMOTE_ADDR'])
151 {
152 $quote_array = explode(",", $getip3['quote_id']);
153 $quote_place = array_search($quote_num, $quote_array);
154 if (in_array($quote_num, $quote_array))
155 {
156 $where_result = explode(",", $getip3[$where]);
157 if (!isset($where_result[$quote_place]))
158 {
159 $where_result[$quote_place] = 1;
160 $where_result = implode(",", $where_result);
161 $setip = "UPDATE rash_tracking SET " . $where . " = \"" . $where_result . "\" WHERE ip = \"" . $_SERVER['REMOTE_ADDR'] . "\"";
162 $setip2 = mysql_query($getip);
163 return 1;
164 } else {
165 return 3;
166 }
167 } else {
168 $setip = "UPDATE rash_tracking SET " . $where . " = CONCAT(" . $where . ",\",1\"), " . $where2 . " = CONCAT(" . $where2 . ",\",0\"), quote_id = CONCAT(quote_id,\"," . $quote_num . "\") WHERE ip = \"" . $_SERVER['REMOTE_ADDR'] . "\"";
169 $setip2 = mysql_query($setip);
170 return 1;
171 }
172 } else {
173 $insip = "INSERT INTO rash_tracking (ip, quote_id, " . $where . ", " . $where2 . ") VALUES (\"" . $_SERVER['REMOTE_ADDR'] . "\", \"" . $quote_num . "\", 1, 0)";
174 $insip2 = mysql_query($insip);
175 return 2;
176 }
177}
178
179function verify_int($subject)
180{
181 $ymax = strlen($subject);
182 $y = 0;
183 while($y < $ymax)
184 {
185 if ((is_int((int)($subject{$y})) && (int)($subject{$y})) || (int)($subject{$y}) === 0 )
186 {
187 $y++;
188 } else {
189 return false;
190 }
191 }
192 return true;
193}
194
195?>
diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..3bedbd7 --- /dev/null +++ b/includes/header.php
@@ -0,0 +1,66 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/header.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25$headerTemp = new FITemplate('header');
26
27if (!isset($_COOKIE['splashOnce']))
28{
29 setcookie('splashOnce', '1');
30
31 $headerTemp->adds_block('SPLASH', array('exi'=>1));
32}
33
34if ((date('G') >= 20) || (date('G') <= 6))
35{
36 $bodyID = 'night';
37} else {
38 $bodyID = 'day';
39}
40$headerTemp->add('BODYID',$bodyID);
41$headerTemp->add('CATEGORY',(isset($pageCategory)) ? $pageCategory : 'none');
42$headerTemp->add('AID',(isset($pageAID)) ? $pageAID : 'none');
43$headerTemp->add('BODYTAGS',(isset($bodyTags)) ? $bodyTags : '');
44$headerTemp->add('HEADTAGS',isset($headtags) ? $headtags : '');
45$headerTemp->add('EXTRATITLE',isset($title) ? ($title . ' - ') : '');
46$headerTemp->add('PAGEID',(isset($pageID)) ? $pageID : 'none');
47
48if (!isset($noMembers))
49{
50 $headerTemp->add('LOWERLOGDATA',strtolower(echoLogData()));
51 $headerTemp->add('REDIRPAGE',rawurlencode($_SERVER['REQUEST_URI']));
52 $headerTemp->add('LOGDATA',echoLogData());
53 if (getUserlevel() < 3)
54 {
55 $headerTemp->adds_block('PANEL',array('exi'=>1));
56 }
57}
58
59if (isset($_POST['message']))
60{
61 $headerTemp->adds_block('MESSAGE',array('MSG' => $_POST['message']));
62}
63
64$headerTemp->display();
65
66?>
diff --git a/includes/hits.php b/includes/hits.php new file mode 100644 index 0000000..053c6b4 --- /dev/null +++ b/includes/hits.php
@@ -0,0 +1,102 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/hits.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25/* Notice:
26
27 This is an old module for counting hits
28 taken directly from the old Four Island.
29 Because of this, it may contain unforseen
30 bugs.
31*/
32
33$getlreset = "SELECT * FROM config WHERE name = 'lastReset'";
34$getlreset2 = mysql_query($getlreset);
35$getlreset3 = mysql_fetch_array($getlreset2);
36if ((floor(time()/86400))>($getlreset3['value']))
37{
38 $sethits = "UPDATE config SET value = " . (floor(time()/86400)) . " WHERE name = 'lastReset'";
39 $sethits2 = mysql_query($sethits);
40 $getips = "SELECT * FROM hits";
41 $getips2 = mysql_query($getips);
42 $i=0;
43 while ($getips3[$i] = mysql_fetch_array($getips2))
44 {
45 $ttime = strtotime($getips3[$i]['lasttime']);
46 $delip = "DELETE FROM hits WHERE id = " . $getips3[$i]['id'];
47 $delip2 = mysql_query($delip);
48 $i++;
49 }
50 $sethits6 = "UPDATE config SET value = 0 WHERE name = 'todayHits'";
51 $sethits7 = mysql_query($sethits6);
52}
53$client_ip = ( !empty($HTTP_SERVER_VARS['REMOTE_ADDR']) ) ? $HTTP_SERVER_VARS['REMOTE_ADDR'] : ( ( !empty($HTTP_ENV_VARS['REMOTE_ADDR']) ) ? $HTTP_ENV_VARS['REMOTE_ADDR'] : getenv('REMOTE_ADDR') );
54$user_ip = $client_ip;
55$getip = "SELECT * FROM hits WHERE ip = '" . $user_ip . "'";
56$getip2 = mysql_query($getip);
57$getip3 = mysql_fetch_array($getip2);
58if ($getip3['ip'] != $user_ip)
59{
60 $gethits = "SELECT * FROM config WHERE name = 'hits'";
61 $gethits2 = mysql_query($gethits);
62 $gethits3 = mysql_fetch_array($gethits2);
63 $sethits = "UPDATE config SET value = " . ($gethits3['value']+1) . " WHERE name = 'hits'";
64 $sethits2 = mysql_query($sethits);
65 $gethits4 = "SELECT * FROM config WHERE name = 'todayHits'";
66 $gethits5 = mysql_query($gethits4);
67 $gethits6 = mysql_fetch_array($gethits5);
68 $sethits4 = "UPDATE config SET value = " . ($gethits6['value']+1) . " WHERE name = 'todayHits'";
69 $sethits5 = mysql_query($sethits4);
70 $gethits7 = "SELECT * FROM config WHERE name = 'imHits'";
71 $gethits8 = mysql_query($gethits7);
72 $gethits9 = mysql_fetch_array($gethits8);
73 $setip = "INSERT INTO hits SET ip = '" . $user_ip . "'";
74 $setip2 = mysql_query($setip);
75 if (isset($_SERVER['HTTP_REFERER']))
76 {
77 $page = $_SERVER['HTTP_REFERER'];
78 } else {
79 $page = '';
80 }
81 $ipdetails = str_pad($client_ip,15,' ') . ' - ' . str_pad($page,61,' ') . ' - ' . str_pad($_SERVER['REQUEST_URI'],27,' ') . ' - ' . time() . chr(13) . chr(10);
82// file_put_contents('/var/www/fourisland/ipdetails.txt',$ipdetails,FILE_APPEND);
83 $milestones = array(100,500,1000,1337,4444,5000,10000,15000,50000,75000,100000,150000,250000,500000,750000,1000000);
84 $i=0;
85 for ($i=0; $i<15; $i++) {
86 if (($gethits3['value']+1)==$milestones[$i]) {
87 file_put_contents('/var/www/fourisland/tophits.txt',$ipdetails,FILE_APPEND);
88 $setmst = 'UPDATE config SET value = "' . time() . '" WHERE name = "milestonetime"';
89 $setmst2 = mysql_query($setmst);
90 $setms = 'UPDATE config SET value = "' . ($gethits3['value']+1) . '" WHERE name = "milestone"';
91 $setms2 = mysql_query($setms);
92 include('includes/header.php');
93?>
94<FONT SIZE="6" COLOR="RED">CONGRADULATIONS! You are the <?php echo($gethits3['value']+1); ?>th person to visit this site! Refresh the page to return to the page you were visiting.</FONT>
95<?php
96 include('includes/footer.php');
97 exit;
98 }
99 }
100}
101
102?>
diff --git a/includes/http.php b/includes/http.php new file mode 100644 index 0000000..d0e55d7 --- /dev/null +++ b/includes/http.php
@@ -0,0 +1,1982 @@
1<?php
2/*
3 * http.php
4 *
5 * @(#) $Header: /home/mlemos/cvsroot/http/http.php,v 1.76 2008/03/18 07:59:05 mlemos Exp $
6 *
7 */
8
9class http_class
10{
11 var $host_name="";
12 var $host_port=0;
13 var $proxy_host_name="";
14 var $proxy_host_port=80;
15 var $socks_host_name = '';
16 var $socks_host_port = 1080;
17 var $socks_version = '5';
18
19 var $protocol="http";
20 var $request_method="GET";
21 var $user_agent='httpclient (http://www.phpclasses.org/httpclient $Revision: 1.76 $)';
22 var $authentication_mechanism="";
23 var $user;
24 var $password;
25 var $realm;
26 var $workstation;
27 var $proxy_authentication_mechanism="";
28 var $proxy_user;
29 var $proxy_password;
30 var $proxy_realm;
31 var $proxy_workstation;
32 var $request_uri="";
33 var $request="";
34 var $request_headers=array();
35 var $request_user;
36 var $request_password;
37 var $request_realm;
38 var $request_workstation;
39 var $proxy_request_user;
40 var $proxy_request_password;
41 var $proxy_request_realm;
42 var $proxy_request_workstation;
43 var $request_body="";
44 var $request_arguments=array();
45 var $protocol_version="1.1";
46 var $timeout=0;
47 var $data_timeout=0;
48 var $debug=0;
49 var $debug_response_body=1;
50 var $html_debug=0;
51 var $support_cookies=1;
52 var $cookies=array();
53 var $error="";
54 var $exclude_address="";
55 var $follow_redirect=0;
56 var $redirection_limit=5;
57 var $response_status="";
58 var $response_message="";
59 var $file_buffer_length=8000;
60 var $force_multipart_form_post=0;
61 var $prefer_curl = 0;
62
63 /* private variables - DO NOT ACCESS */
64
65 var $state="Disconnected";
66 var $use_curl=0;
67 var $connection=0;
68 var $content_length=0;
69 var $response="";
70 var $read_response=0;
71 var $read_length=0;
72 var $request_host="";
73 var $next_token="";
74 var $redirection_level=0;
75 var $chunked=0;
76 var $remaining_chunk=0;
77 var $last_chunk_read=0;
78 var $months=array(
79 "Jan"=>"01",
80 "Feb"=>"02",
81 "Mar"=>"03",
82 "Apr"=>"04",
83 "May"=>"05",
84 "Jun"=>"06",
85 "Jul"=>"07",
86 "Aug"=>"08",
87 "Sep"=>"09",
88 "Oct"=>"10",
89 "Nov"=>"11",
90 "Dec"=>"12");
91 var $session='';
92 var $connection_close=0;
93
94 /* Private methods - DO NOT CALL */
95
96 Function Tokenize($string,$separator="")
97 {
98 if(!strcmp($separator,""))
99 {
100 $separator=$string;
101 $string=$this->next_token;
102 }
103 for($character=0;$character<strlen($separator);$character++)
104 {
105 if(GetType($position=strpos($string,$separator[$character]))=="integer")
106 $found=(IsSet($found) ? min($found,$position) : $position);
107 }
108 if(IsSet($found))
109 {
110 $this->next_token=substr($string,$found+1);
111 return(substr($string,0,$found));
112 }
113 else
114 {
115 $this->next_token="";
116 return($string);
117 }
118 }
119
120 Function CookieEncode($value, $name)
121 {
122 return($name ? str_replace("=", "%25", $value) : str_replace(";", "%3B", $value));
123 }
124
125 Function SetError($error)
126 {
127 return($this->error=$error);
128 }
129
130 Function SetPHPError($error, &$php_error_message)
131 {
132 if(IsSet($php_error_message)
133 && strlen($php_error_message))
134 $error.=": ".$php_error_message;
135 return($this->SetError($error));
136 }
137
138 Function SetDataAccessError($error,$check_connection=0)
139 {
140 $this->error=$error;
141 if(!$this->use_curl
142 && function_exists("socket_get_status"))
143 {
144 $status=socket_get_status($this->connection);
145 if($status["timed_out"])
146 $this->error.=": data access time out";
147 elseif($status["eof"])
148 {
149 if($check_connection)
150 $this->error="";
151 else
152 $this->error.=": the server disconnected";
153 }
154 }
155 }
156
157 Function OutputDebug($message)
158 {
159 $message.="\n";
160 if($this->html_debug)
161 $message=str_replace("\n","<br />\n",HtmlEntities($message));
162 echo $message;
163 flush();
164 }
165
166 Function GetLine()
167 {
168 for($line="";;)
169 {
170 if($this->use_curl)
171 {
172 $eol=strpos($this->response,"\n",$this->read_response);
173 $data=($eol ? substr($this->response,$this->read_response,$eol+1-$this->read_response) : "");
174 $this->read_response+=strlen($data);
175 }
176 else
177 {
178 if(feof($this->connection))
179 {
180 $this->SetDataAccessError("reached the end of data while reading from the HTTP server connection");
181 return(0);
182 }
183 $data=fgets($this->connection,100);
184 }
185 if(GetType($data)!="string"
186 || strlen($data)==0)
187 {
188 $this->SetDataAccessError("it was not possible to read line from the HTTP server");
189 return(0);
190 }
191 $line.=$data;
192 $length=strlen($line);
193 if($length
194 && !strcmp(substr($line,$length-1,1),"\n"))
195 {
196 $length-=(($length>=2 && !strcmp(substr($line,$length-2,1),"\r")) ? 2 : 1);
197 $line=substr($line,0,$length);
198 if($this->debug)
199 $this->OutputDebug("S $line");
200 return($line);
201 }
202 }
203 }
204
205 Function PutLine($line)
206 {
207 if($this->debug)
208 $this->OutputDebug("C $line");
209 if(!fputs($this->connection,$line."\r\n"))
210 {
211 $this->SetDataAccessError("it was not possible to send a line to the HTTP server");
212 return(0);
213 }
214 return(1);
215 }
216
217 Function PutData($data)
218 {
219 if(strlen($data))
220 {
221 if($this->debug)
222 $this->OutputDebug('C '.$data);
223 if(!fputs($this->connection,$data))
224 {
225 $this->SetDataAccessError("it was not possible to send data to the HTTP server");
226 return(0);
227 }
228 }
229 return(1);
230 }
231
232 Function FlushData()
233 {
234 if(!fflush($this->connection))
235 {
236 $this->SetDataAccessError("it was not possible to send data to the HTTP server");
237 return(0);
238 }
239 return(1);
240 }
241
242 Function ReadChunkSize()
243 {
244 if($this->remaining_chunk==0)
245 {
246 $debug=$this->debug;
247 if(!$this->debug_response_body)
248 $this->debug=0;
249 $line=$this->GetLine();
250 $this->debug=$debug;
251 if(GetType($line)!="string")
252 return($this->SetError("4 could not read chunk start: ".$this->error));
253 $this->remaining_chunk=hexdec($line);
254 }
255 return("");
256 }
257
258 Function ReadBytes($length)
259 {
260 if($this->use_curl)
261 {
262 $bytes=substr($this->response,$this->read_response,min($length,strlen($this->response)-$this->read_response));
263 $this->read_response+=strlen($bytes);
264 if($this->debug
265 && $this->debug_response_body
266 && strlen($bytes))
267 $this->OutputDebug("S ".$bytes);
268 }
269 else
270 {
271 if($this->chunked)
272 {
273 for($bytes="",$remaining=$length;$remaining;)
274 {
275 if(strlen($this->ReadChunkSize()))
276 return("");
277 if($this->remaining_chunk==0)
278 {
279 $this->last_chunk_read=1;
280 break;
281 }
282 $ask=min($this->remaining_chunk,$remaining);
283 $chunk=@fread($this->connection,$ask);
284 $read=strlen($chunk);
285 if($read==0)
286 {
287 $this->SetDataAccessError("it was not possible to read data chunk from the HTTP server");
288 return("");
289 }
290 if($this->debug
291 && $this->debug_response_body)
292 $this->OutputDebug("S ".$chunk);
293 $bytes.=$chunk;
294 $this->remaining_chunk-=$read;
295 $remaining-=$read;
296 if($this->remaining_chunk==0)
297 {
298 if(feof($this->connection))
299 return($this->SetError("reached the end of data while reading the end of data chunk mark from the HTTP server"));
300 $data=@fread($this->connection,2);
301 if(strcmp($data,"\r\n"))
302 {
303 $this->SetDataAccessError("it was not possible to read end of data chunk from the HTTP server");
304 return("");
305 }
306 }
307 }
308 }
309 else
310 {
311 $bytes=@fread($this->connection,$length);
312 if(strlen($bytes))
313 {
314 if($this->debug
315 && $this->debug_response_body)
316 $this->OutputDebug("S ".$bytes);
317 }
318 else
319 $this->SetDataAccessError("it was not possible to read data from the HTTP server", $this->connection_close);
320 }
321 }
322 return($bytes);
323 }
324
325 Function EndOfInput()
326 {
327 if($this->use_curl)
328 return($this->read_response>=strlen($this->response));
329 if($this->chunked)
330 return($this->last_chunk_read);
331 return(feof($this->connection));
332 }
333
334 Function Resolve($domain, &$ip, $server_type)
335 {
336 if(ereg('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$',$domain))
337 $ip=$domain;
338 else
339 {
340 if($this->debug)
341 $this->OutputDebug('Resolving '.$server_type.' server domain "'.$domain.'"...');
342 if(!strcmp($ip=@gethostbyname($domain),$domain))
343 $ip="";
344 }
345 if(strlen($ip)==0
346 || (strlen($this->exclude_address)
347 && !strcmp(@gethostbyname($this->exclude_address),$ip)))
348 return($this->SetError("could not resolve the host domain \"".$domain."\""));
349 return('');
350 }
351
352 Function Connect($host_name, $host_port, $ssl, $server_type = 'HTTP')
353 {
354 $domain=$host_name;
355 $port = $host_port;
356 if(strlen($error = $this->Resolve($domain, $ip, $server_type)))
357 return($error);
358 if(strlen($this->socks_host_name))
359 {
360 switch($this->socks_version)
361 {
362 case '4':
363 $version = 4;
364 break;
365 case '5':
366 $version = 5;
367 break;
368 default:
369 return('it was not specified a supported SOCKS protocol version');
370 break;
371 }
372 $host_ip = $ip;
373 $port = $this->socks_host_port;
374 $host_server_type = $server_type;
375 $server_type = 'SOCKS';
376 if(strlen($error = $this->Resolve($this->socks_host_name, $ip, $server_type)))
377 return($error);
378 }
379 if($this->debug)
380 $this->OutputDebug('Connecting to '.$server_type.' server IP '.$ip.' port '.$port.'...');
381 if($ssl)
382 $ip="ssl://".$ip;
383 if(($this->connection=($this->timeout ? @fsockopen($ip, $port, $errno, $error, $this->timeout) : @fsockopen($ip, $port, $errno)))==0)
384 {
385 switch($errno)
386 {
387 case -3:
388 return($this->SetError("-3 socket could not be created"));
389 case -4:
390 return($this->SetError("-4 dns lookup on hostname \"".$host_name."\" failed"));
391 case -5:
392 return($this->SetError("-5 connection refused or timed out"));
393 case -6:
394 return($this->SetError("-6 fdopen() call failed"));
395 case -7:
396 return($this->SetError("-7 setvbuf() call failed"));
397 default:
398 return($this->SetPHPError($errno." could not connect to the host \"".$host_name."\"",$php_errormsg));
399 }
400 }
401 else
402 {
403 if($this->data_timeout
404 && function_exists("socket_set_timeout"))
405 socket_set_timeout($this->connection,$this->data_timeout,0);
406 if(strlen($this->socks_host_name))
407 {
408 if($this->debug)
409 $this->OutputDebug('Connected to the SOCKS server '.$this->socks_host_name);
410 $send_error = 'it was not possible to send data to the SOCKS server';
411 $receive_error = 'it was not possible to receive data from the SOCKS server';
412 switch($version)
413 {
414 case 4:
415 $command = 1;
416 if(!fputs($this->connection, chr($version).chr($command).pack('nN', $host_port, ip2long($host_ip)).$this->user.Chr(0)))
417 $error = $this->SetDataAccessError($send_error);
418 else
419 {
420 $response = fgets($this->connection, 9);
421 if(strlen($response) != 8)
422 $error = $this->SetDataAccessError($receive_error);
423 else
424 {
425 $socks_errors = array(
426 "\x5a"=>'',
427 "\x5b"=>'request rejected',
428 "\x5c"=>'request failed because client is not running identd (or not reachable from the server)',
429 "\x5d"=>'request failed because client\'s identd could not confirm the user ID string in the request',
430 );
431 $error_code = $response[1];
432 $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
433 if(strlen($error))
434 $error = 'SOCKS error: '.$error;
435 }
436 }
437 break;
438 case 5:
439 if($this->debug)
440 $this->OutputDebug('Negotiating the authentication method ...');
441 $methods = 1;
442 $method = 0;
443 if(!fputs($this->connection, chr($version).chr($methods).chr($method)))
444 $error = $this->SetDataAccessError($send_error);
445 else
446 {
447 $response = fgets($this->connection, 3);
448 if(strlen($response) != 2)
449 $error = $this->SetDataAccessError($receive_error);
450 elseif(Ord($response[1]) != $method)
451 $error = 'the SOCKS server requires an authentication method that is not yet supported';
452 else
453 {
454 if($this->debug)
455 $this->OutputDebug('Connecting to '.$host_server_type.' server IP '.$host_ip.' port '.$host_port.'...');
456 $command = 1;
457 $address_type = 1;
458 if(!fputs($this->connection, chr($version).chr($command)."\x00".chr($address_type).pack('Nn', ip2long($host_ip), $host_port)))
459 $error = $this->SetDataAccessError($send_error);
460 else
461 {
462 $response = fgets($this->connection, 11);
463 if(strlen($response) != 10)
464 $error = $this->SetDataAccessError($receive_error);
465 else
466 {
467 $socks_errors = array(
468 "\x00"=>'',
469 "\x01"=>'general SOCKS server failure',
470 "\x02"=>'connection not allowed by ruleset',
471 "\x03"=>'Network unreachable',
472 "\x04"=>'Host unreachable',
473 "\x05"=>'Connection refused',
474 "\x06"=>'TTL expired',
475 "\x07"=>'Command not supported',
476 "\x08"=>'Address type not supported'
477 );
478 $error_code = $response[1];
479 $error = (IsSet($socks_errors[$error_code]) ? $socks_errors[$error_code] : 'unknown');
480 if(strlen($error))
481 $error = 'SOCKS error: '.$error;
482 }
483 }
484 }
485 }
486 break;
487 default:
488 $error = 'support for SOCKS protocol version '.$this->socks_version.' is not yet implemented';
489 break;
490 }
491 if(strlen($error))
492 {
493 fclose($this->connection);
494 return($error);
495 }
496 }
497 if($this->debug)
498 $this->OutputDebug("Connected to $host_name");
499 if(strlen($this->proxy_host_name)
500 && !strcmp(strtolower($this->protocol), 'https'))
501 {
502 if(function_exists('stream_socket_enable_crypto')
503 && in_array('ssl', stream_get_transports()))
504 $this->state = "ConnectedToProxy";
505 else
506 {
507 $this->OutputDebug("It is not possible to start SSL after connecting to the proxy server. If the proxy refuses to forward the SSL request, you may need to upgrade to PHP 5.1 or later with OpenSSL support enabled.");
508 $this->state="Connected";
509 }
510 }
511 else
512 $this->state="Connected";
513 return("");
514 }
515 }
516
517 Function Disconnect()
518 {
519 if($this->debug)
520 $this->OutputDebug("Disconnected from ".$this->host_name);
521 if($this->use_curl)
522 {
523 curl_close($this->connection);
524 $this->response="";
525 }
526 else
527 fclose($this->connection);
528 $this->state="Disconnected";
529 return("");
530 }
531
532 /* Public methods */
533
534 Function GetRequestArguments($url, &$arguments)
535 {
536 if(strlen($this->error))
537 return($this->error);
538 $arguments=array();
539 $parameters=@parse_url($url);
540 if(!$parameters)
541 return($this->SetError("it was not specified a valid URL"));
542 if(!IsSet($parameters["scheme"]))
543 return($this->SetError("it was not specified the protocol type argument"));
544 switch(strtolower($parameters["scheme"]))
545 {
546 case "http":
547 case "https":
548 $arguments["Protocol"]=$parameters["scheme"];
549 break;
550 default:
551 return($parameters["scheme"]." connection scheme is not yet supported");
552 }
553 if(!IsSet($parameters["host"]))
554 return($this->SetError("it was not specified the connection host argument"));
555 $arguments["HostName"]=$parameters["host"];
556 $arguments["Headers"]=array("Host"=>$parameters["host"].(IsSet($parameters["port"]) ? ":".$parameters["port"] : ""));
557 if(IsSet($parameters["user"]))
558 {
559 $arguments["AuthUser"]=UrlDecode($parameters["user"]);
560 if(!IsSet($parameters["pass"]))
561 $arguments["AuthPassword"]="";
562 }
563 if(IsSet($parameters["pass"]))
564 {
565 if(!IsSet($parameters["user"]))
566 $arguments["AuthUser"]="";
567 $arguments["AuthPassword"]=UrlDecode($parameters["pass"]);
568 }
569 if(IsSet($parameters["port"]))
570 {
571 if(strcmp($parameters["port"],strval(intval($parameters["port"]))))
572 return($this->SetError("it was not specified a valid connection host argument"));
573 $arguments["HostPort"]=intval($parameters["port"]);
574 }
575 else
576 $arguments["HostPort"]=0;
577 $arguments["RequestURI"]=(IsSet($parameters["path"]) ? $parameters["path"] : "/").(IsSet($parameters["query"]) ? "?".$parameters["query"] : "");
578 if(strlen($this->user_agent))
579 $arguments["Headers"]["User-Agent"]=$this->user_agent;
580 return("");
581 }
582
583 Function Open($arguments)
584 {
585 if(strlen($this->error))
586 return($this->error);
587 if($this->state!="Disconnected")
588 return("1 already connected");
589 if(IsSet($arguments["HostName"]))
590 $this->host_name=$arguments["HostName"];
591 if(IsSet($arguments["HostPort"]))
592 $this->host_port=$arguments["HostPort"];
593 if(IsSet($arguments["ProxyHostName"]))
594 $this->proxy_host_name=$arguments["ProxyHostName"];
595 if(IsSet($arguments["ProxyHostPort"]))
596 $this->proxy_host_port=$arguments["ProxyHostPort"];
597 if(IsSet($arguments["SOCKSHostName"]))
598 $this->socks_host_name=$arguments["SOCKSHostName"];
599 if(IsSet($arguments["SOCKSHostPort"]))
600 $this->socks_host_port=$arguments["SOCKSHostPort"];
601 if(IsSet($arguments["SOCKSVersion"]))
602 $this->socks_version=$arguments["SOCKSVersion"];
603 if(IsSet($arguments["Protocol"]))
604 $this->protocol=$arguments["Protocol"];
605 switch(strtolower($this->protocol))
606 {
607 case "http":
608 $default_port=80;
609 break;
610 case "https":
611 $default_port=443;
612 break;
613 default:
614 return($this->SetError("2 it was not specified a valid connection protocol"));
615 }
616 if(strlen($this->proxy_host_name)==0)
617 {
618 if(strlen($this->host_name)==0)
619 return($this->SetError("2 it was not specified a valid hostname"));
620 $host_name=$this->host_name;
621 $host_port=($this->host_port ? $this->host_port : $default_port);
622 $server_type = 'HTTP';
623 }
624 else
625 {
626 $host_name=$this->proxy_host_name;
627 $host_port=$this->proxy_host_port;
628 $server_type = 'HTTP proxy';
629 }
630 $ssl=(strtolower($this->protocol)=="https" && strlen($this->proxy_host_name)==0);
631 if($ssl
632 && strlen($this->socks_host_name))
633 return($this->SetError('establishing SSL connections via a SOCKS server is not yet supported'));
634 $this->use_curl=($ssl && $this->prefer_curl && function_exists("curl_init"));
635 if($this->debug)
636 $this->OutputDebug("Connecting to ".$this->host_name);
637 if($this->use_curl)
638 {
639 $error=(($this->connection=curl_init($this->protocol."://".$this->host_name.($host_port==$default_port ? "" : ":".strval($host_port))."/")) ? "" : "Could not initialize a CURL session");
640 if(strlen($error)==0)
641 {
642 if(IsSet($arguments["SSLCertificateFile"]))
643 curl_setopt($this->connection,CURLOPT_SSLCERT,$arguments["SSLCertificateFile"]);
644 if(IsSet($arguments["SSLCertificatePassword"]))
645 curl_setopt($this->connection,CURLOPT_SSLCERTPASSWD,$arguments["SSLCertificatePassword"]);
646 if(IsSet($arguments["SSLKeyFile"]))
647 curl_setopt($this->connection,CURLOPT_SSLKEY,$arguments["SSLKeyFile"]);
648 if(IsSet($arguments["SSLKeyPassword"]))
649 curl_setopt($this->connection,CURLOPT_SSLKEYPASSWD,$arguments["SSLKeyPassword"]);
650 }
651 $this->state="Connected";
652 }
653 else
654 {
655 $error="";
656 if(strlen($this->proxy_host_name)
657 && (IsSet($arguments["SSLCertificateFile"])
658 || IsSet($arguments["SSLCertificateFile"])))
659 $error="establishing SSL connections using certificates or private keys via non-SSL proxies is not supported";
660 else
661 {
662 if($ssl)
663 {
664 if(IsSet($arguments["SSLCertificateFile"]))
665 $error="establishing SSL connections using certificates is only supported when the cURL extension is enabled";
666 elseif(IsSet($arguments["SSLKeyFile"]))
667 $error="establishing SSL connections using a private key is only supported when the cURL extension is enabled";
668 else
669 {
670 $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
671 $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]);
672 if($php_version<4003000)
673 $error="establishing SSL connections requires at least PHP version 4.3.0 or having the cURL extension enabled";
674 elseif(!function_exists("extension_loaded")
675 || !extension_loaded("openssl"))
676 $error="establishing SSL connections requires the OpenSSL extension enabled";
677 }
678 }
679 if(strlen($error)==0)
680 $error=$this->Connect($host_name, $host_port, $ssl, $server_type);
681 }
682 }
683 if(strlen($error))
684 return($this->SetError($error));
685 $this->session=md5(uniqid(""));
686 return("");
687 }
688
689 Function Close()
690 {
691 if($this->state=="Disconnected")
692 return("1 already disconnected");
693 $error=$this->Disconnect();
694 if(strlen($error)==0)
695 $this->state="Disconnected";
696 return($error);
697 }
698
699 Function PickCookies(&$cookies,$secure)
700 {
701 if(IsSet($this->cookies[$secure]))
702 {
703 $now=gmdate("Y-m-d H-i-s");
704 for($domain=0,Reset($this->cookies[$secure]);$domain<count($this->cookies[$secure]);Next($this->cookies[$secure]),$domain++)
705 {
706 $domain_pattern=Key($this->cookies[$secure]);
707 $match=strlen($this->request_host)-strlen($domain_pattern);
708 if($match>=0
709 && !strcmp($domain_pattern,substr($this->request_host,$match))
710 && ($match==0
711 || $domain_pattern[0]=="."
712 || $this->request_host[$match-1]=="."))
713 {
714 for(Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_part<count($this->cookies[$secure][$domain_pattern]);Next($this->cookies[$secure][$domain_pattern]),$path_part++)
715 {
716 $path=Key($this->cookies[$secure][$domain_pattern]);
717 if(strlen($this->request_uri)>=strlen($path)
718 && substr($this->request_uri,0,strlen($path))==$path)
719 {
720 for(Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<count($this->cookies[$secure][$domain_pattern][$path]);Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++)
721 {
722 $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]);
723 $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
724 if($expires==""
725 || strcmp($now,$expires)<0)
726 $cookies[$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name];
727 }
728 }
729 }
730 }
731 }
732 }
733 }
734
735 Function GetFileDefinition($file, &$definition)
736 {
737 $name="";
738 if(IsSet($file["FileName"]))
739 $name=basename($file["FileName"]);
740 if(IsSet($file["Name"]))
741 $name=$file["Name"];
742 if(strlen($name)==0)
743 return("it was not specified the file part name");
744 if(IsSet($file["Content-Type"]))
745 {
746 $content_type=$file["Content-Type"];
747 $type=$this->Tokenize(strtolower($content_type),"/");
748 $sub_type=$this->Tokenize("");
749 switch($type)
750 {
751 case "text":
752 case "image":
753 case "audio":
754 case "video":
755 case "application":
756 case "message":
757 break;
758 case "automatic":
759 switch($sub_type)
760 {
761 case "name":
762 switch(GetType($dot=strrpos($name,"."))=="integer" ? strtolower(substr($name,$dot)) : "")
763 {
764 case ".xls":
765 $content_type="application/excel";
766 break;
767 case ".hqx":
768 $content_type="application/macbinhex40";
769 break;
770 case ".doc":
771 case ".dot":
772 case ".wrd":
773 $content_type="application/msword";
774 break;
775 case ".pdf":
776 $content_type="application/pdf";
777 break;
778 case ".pgp":
779 $content_type="application/pgp";
780 break;
781 case ".ps":
782 case ".eps":
783 case ".ai":
784 $content_type="application/postscript";
785 break;
786 case ".ppt":
787 $content_type="application/powerpoint";
788 break;
789 case ".rtf":
790 $content_type="application/rtf";
791 break;
792 case ".tgz":
793 case ".gtar":
794 $content_type="application/x-gtar";
795 break;
796 case ".gz":
797 $content_type="application/x-gzip";
798 break;
799 case ".php":
800 case ".php3":
801 $content_type="application/x-httpd-php";
802 break;
803 case ".js":
804 $content_type="application/x-javascript";
805 break;
806 case ".ppd":
807 case ".psd":
808 $content_type="application/x-photoshop";
809 break;
810 case ".swf":
811 case ".swc":
812 case ".rf":
813 $content_type="application/x-shockwave-flash";
814 break;
815 case ".tar":
816 $content_type="application/x-tar";
817 break;
818 case ".zip":
819 $content_type="application/zip";
820 break;
821 case ".mid":
822 case ".midi":
823 case ".kar":
824 $content_type="audio/midi";
825 break;
826 case ".mp2":
827 case ".mp3":
828 case ".mpga":
829 $content_type="audio/mpeg";
830 break;
831 case ".ra":
832 $content_type="audio/x-realaudio";
833 break;
834 case ".wav":
835 $content_type="audio/wav";
836 break;
837 case ".bmp":
838 $content_type="image/bitmap";
839 break;
840 case ".gif":
841 $content_type="image/gif";
842 break;
843 case ".iff":
844 $content_type="image/iff";
845 break;
846 case ".jb2":
847 $content_type="image/jb2";
848 break;
849 case ".jpg":
850 case ".jpe":
851 case ".jpeg":
852 $content_type="image/jpeg";
853 break;
854 case ".jpx":
855 $content_type="image/jpx";
856 break;
857 case ".png":
858 $content_type="image/png";
859 break;
860 case ".tif":
861 case ".tiff":
862 $content_type="image/tiff";
863 break;
864 case ".wbmp":
865 $content_type="image/vnd.wap.wbmp";
866 break;
867 case ".xbm":
868 $content_type="image/xbm";
869 break;
870 case ".css":
871 $content_type="text/css";
872 break;
873 case ".txt":
874 $content_type="text/plain";
875 break;
876 case ".htm":
877 case ".html":
878 $content_type="text/html";
879 break;
880 case ".xml":
881 $content_type="text/xml";
882 break;
883 case ".mpg":
884 case ".mpe":
885 case ".mpeg":
886 $content_type="video/mpeg";
887 break;
888 case ".qt":
889 case ".mov":
890 $content_type="video/quicktime";
891 break;
892 case ".avi":
893 $content_type="video/x-ms-video";
894 break;
895 case ".eml":
896 $content_type="message/rfc822";
897 break;
898 default:
899 $content_type="application/octet-stream";
900 break;
901 }
902 break;
903 default:
904 return($content_type." is not a supported automatic content type detection method");
905 }
906 break;
907 default:
908 return($content_type." is not a supported file content type");
909 }
910 }
911 else
912 $content_type="application/octet-stream";
913 $definition=array(
914 "Content-Type"=>$content_type,
915 "NAME"=>$name
916 );
917 if(IsSet($file["FileName"]))
918 {
919 if(GetType($length=@filesize($file["FileName"]))!="integer")
920 {
921 $error="it was not possible to determine the length of the file ".$file["FileName"];
922 if(IsSet($php_errormsg)
923 && strlen($php_errormsg))
924 $error.=": ".$php_errormsg;
925 if(!file_exists($file["FileName"]))
926 $error="it was not possible to access the file ".$file["FileName"];
927 return($error);
928 }
929 $definition["FILENAME"]=$file["FileName"];
930 $definition["Content-Length"]=$length;
931 }
932 elseif(IsSet($file["Data"]))
933 $definition["Content-Length"]=strlen($definition["DATA"]=$file["Data"]);
934 else
935 return("it was not specified a valid file name");
936 return("");
937 }
938
939 Function ConnectFromProxy($arguments, &$headers)
940 {
941 if(!$this->PutLine('CONNECT '.$this->host_name.':'.($this->host_port ? $this->host_port : 443).' HTTP/1.0')
942 || (strlen($this->user_agent)
943 && !$this->PutLine('User-Agent: '.$this->user_agent))
944 || (IsSet($arguments['Headers']['Proxy-Authorization'])
945 && !$this->PutLine('Proxy-Authorization: '.$arguments['Headers']['Proxy-Authorization']))
946 || !$this->PutLine(''))
947 {
948 $this->Disconnect();
949 return($this->error);
950 }
951 $this->state = "ConnectSent";
952 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
953 return($error);
954 $proxy_authorization="";
955 while(!strcmp($this->response_status, "100"))
956 {
957 $this->state="ConnectSent";
958 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
959 return($error);
960 }
961 switch($this->response_status)
962 {
963 case "200":
964 if(!@stream_socket_enable_crypto($this->connection, 1, STREAM_CRYPTO_METHOD_SSLv23_CLIENT))
965 {
966 $this->SetPHPError('it was not possible to start a SSL encrypted connection via this proxy', $php_errormsg);
967 $this->Disconnect();
968 return($this->error);
969 }
970 $this->state = "Connected";
971 break;
972 case "407":
973 if(strlen($error=$this->Authenticate($headers, -1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation)))
974 return($error);
975 break;
976 default:
977 return($this->SetError("unable to send request via proxy"));
978 }
979 return("");
980 }
981
982 Function SendRequest($arguments)
983 {
984 if(strlen($this->error))
985 return($this->error);
986 if(IsSet($arguments["ProxyUser"]))
987 $this->proxy_request_user=$arguments["ProxyUser"];
988 elseif(IsSet($this->proxy_user))
989 $this->proxy_request_user=$this->proxy_user;
990 if(IsSet($arguments["ProxyPassword"]))
991 $this->proxy_request_password=$arguments["ProxyPassword"];
992 elseif(IsSet($this->proxy_password))
993 $this->proxy_request_password=$this->proxy_password;
994 if(IsSet($arguments["ProxyRealm"]))
995 $this->proxy_request_realm=$arguments["ProxyRealm"];
996 elseif(IsSet($this->proxy_realm))
997 $this->proxy_request_realm=$this->proxy_realm;
998 if(IsSet($arguments["ProxyWorkstation"]))
999 $this->proxy_request_workstation=$arguments["ProxyWorkstation"];
1000 elseif(IsSet($this->proxy_workstation))
1001 $this->proxy_request_workstation=$this->proxy_workstation;
1002 switch($this->state)
1003 {
1004 case "Disconnected":
1005 return($this->SetError("1 connection was not yet established"));
1006 case "Connected":
1007 $connect = 0;
1008 break;
1009 case "ConnectedToProxy":
1010 if(strlen($error = $this->ConnectFromProxy($arguments, $headers)))
1011 return($error);
1012 $connect = 1;
1013 break;
1014 default:
1015 return($this->SetError("2 can not send request in the current connection state"));
1016 }
1017 if(IsSet($arguments["RequestMethod"]))
1018 $this->request_method=$arguments["RequestMethod"];
1019 if(IsSet($arguments["User-Agent"]))
1020 $this->user_agent=$arguments["User-Agent"];
1021 if(!IsSet($arguments["Headers"]["User-Agent"])
1022 && strlen($this->user_agent))
1023 $arguments["Headers"]["User-Agent"]=$this->user_agent;
1024 if(strlen($this->request_method)==0)
1025 return($this->SetError("3 it was not specified a valid request method"));
1026 if(IsSet($arguments["RequestURI"]))
1027 $this->request_uri=$arguments["RequestURI"];
1028 if(strlen($this->request_uri)==0
1029 || substr($this->request_uri,0,1)!="/")
1030 return($this->SetError("4 it was not specified a valid request URI"));
1031 $this->request_arguments=$arguments;
1032 $this->request_headers=(IsSet($arguments["Headers"]) ? $arguments["Headers"] : array());
1033 $body_length=0;
1034 $this->request_body="";
1035 $get_body=1;
1036 if($this->request_method=="POST"
1037 || $this->request_method=="PUT")
1038 {
1039 if(IsSet($arguments['StreamRequest']))
1040 {
1041 $get_body = 0;
1042 $this->request_headers["Transfer-Encoding"]="chunked";
1043 }
1044 elseif(IsSet($arguments["PostFiles"])
1045 || ($this->force_multipart_form_post
1046 && IsSet($arguments["PostValues"])))
1047 {
1048 $boundary="--".md5(uniqid(time()));
1049 $this->request_headers["Content-Type"]="multipart/form-data; boundary=".$boundary.(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1050 $post_parts=array();
1051 if(IsSet($arguments["PostValues"]))
1052 {
1053 $values=$arguments["PostValues"];
1054 if(GetType($values)!="array")
1055 return($this->SetError("5 it was not specified a valid POST method values array"));
1056 for(Reset($values),$value=0;$value<count($values);Next($values),$value++)
1057 {
1058 $input=Key($values);
1059 $headers="--".$boundary."\r\nContent-Disposition: form-data; name=\"".$input."\"\r\n\r\n";
1060 $data=$values[$input];
1061 $post_parts[]=array("HEADERS"=>$headers,"DATA"=>$data);
1062 $body_length+=strlen($headers)+strlen($data)+strlen("\r\n");
1063 }
1064 }
1065 $body_length+=strlen("--".$boundary."--\r\n");
1066 $files=(IsSet($arguments["PostFiles"]) ? $arguments["PostFiles"] : array());
1067 Reset($files);
1068 $end=(GetType($input=Key($files))!="string");
1069 for(;!$end;)
1070 {
1071 if(strlen($error=$this->GetFileDefinition($files[$input],$definition)))
1072 return("3 ".$error);
1073 $headers="--".$boundary."\r\nContent-Disposition: form-data; name=\"".$input."\"; filename=\"".$definition["NAME"]."\"\r\nContent-Type: ".$definition["Content-Type"]."\r\n\r\n";
1074 $part=count($post_parts);
1075 $post_parts[$part]=array("HEADERS"=>$headers);
1076 if(IsSet($definition["FILENAME"]))
1077 {
1078 $post_parts[$part]["FILENAME"]=$definition["FILENAME"];
1079 $data="";
1080 }
1081 else
1082 $data=$definition["DATA"];
1083 $post_parts[$part]["DATA"]=$data;
1084 $body_length+=strlen($headers)+$definition["Content-Length"]+strlen("\r\n");
1085 Next($files);
1086 $end=(GetType($input=Key($files))!="string");
1087 }
1088 $get_body=0;
1089 }
1090 elseif(IsSet($arguments["PostValues"]))
1091 {
1092 $values=$arguments["PostValues"];
1093 if(GetType($values)!="array")
1094 return($this->SetError("5 it was not specified a valid POST method values array"));
1095 for(Reset($values),$value=0;$value<count($values);Next($values),$value++)
1096 {
1097 $k=Key($values);
1098 if(GetType($values[$k])=="array")
1099 {
1100 for($v = 0; $v < count($values[$k]); $v++)
1101 {
1102 if($value+$v>0)
1103 $this->request_body.="&";
1104 $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k][$v]);
1105 }
1106 }
1107 else
1108 {
1109 if($value>0)
1110 $this->request_body.="&";
1111 $this->request_body.=UrlEncode($k)."=".UrlEncode($values[$k]);
1112 }
1113 }
1114 $this->request_headers["Content-Type"]="application/x-www-form-urlencoded".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1115 $get_body=0;
1116 }
1117 }
1118 if($get_body
1119 && (IsSet($arguments["Body"])
1120 || IsSet($arguments["BodyStream"])))
1121 {
1122 if(IsSet($arguments["Body"]))
1123 $this->request_body=$arguments["Body"];
1124 else
1125 {
1126 $stream=$arguments["BodyStream"];
1127 $this->request_body="";
1128 for($part=0; $part<count($stream); $part++)
1129 {
1130 if(IsSet($stream[$part]["Data"]))
1131 $this->request_body.=$stream[$part]["Data"];
1132 elseif(IsSet($stream[$part]["File"]))
1133 {
1134 if(!($file=@fopen($stream[$part]["File"],"rb")))
1135 return($this->SetPHPError("could not open upload file ".$stream[$part]["File"], $php_errormsg));
1136 while(!feof($file))
1137 {
1138 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1139 {
1140 $error=$this->SetPHPError("could not read body stream file ".$stream[$part]["File"], $php_errormsg);
1141 fclose($file);
1142 return($error);
1143 }
1144 $this->request_body.=$block;
1145 }
1146 fclose($file);
1147 }
1148 else
1149 return("5 it was not specified a valid file or data body stream element at position ".$part);
1150 }
1151 }
1152 if(!IsSet($this->request_headers["Content-Type"]))
1153 $this->request_headers["Content-Type"]="application/octet-stream".(IsSet($arguments["CharSet"]) ? "; charset=".$arguments["CharSet"] : "");
1154 }
1155 if(IsSet($arguments["AuthUser"]))
1156 $this->request_user=$arguments["AuthUser"];
1157 elseif(IsSet($this->user))
1158 $this->request_user=$this->user;
1159 if(IsSet($arguments["AuthPassword"]))
1160 $this->request_password=$arguments["AuthPassword"];
1161 elseif(IsSet($this->password))
1162 $this->request_password=$this->password;
1163 if(IsSet($arguments["AuthRealm"]))
1164 $this->request_realm=$arguments["AuthRealm"];
1165 elseif(IsSet($this->realm))
1166 $this->request_realm=$this->realm;
1167 if(IsSet($arguments["AuthWorkstation"]))
1168 $this->request_workstation=$arguments["AuthWorkstation"];
1169 elseif(IsSet($this->workstation))
1170 $this->request_workstation=$this->workstation;
1171 if(strlen($this->proxy_host_name)==0
1172 || $connect)
1173 $request_uri=$this->request_uri;
1174 else
1175 {
1176 switch(strtolower($this->protocol))
1177 {
1178 case "http":
1179 $default_port=80;
1180 break;
1181 case "https":
1182 $default_port=443;
1183 break;
1184 }
1185 $request_uri=strtolower($this->protocol)."://".$this->host_name.(($this->host_port==0 || $this->host_port==$default_port) ? "" : ":".$this->host_port).$this->request_uri;
1186 }
1187 if($this->use_curl)
1188 {
1189 $version=(GetType($v=curl_version())=="array" ? (IsSet($v["version"]) ? $v["version"] : "0.0.0") : (ereg("^libcurl/([0-9]+\\.[0-9]+\\.[0-9]+)",$v,$m) ? $m[1] : "0.0.0"));
1190 $curl_version=100000*intval($this->Tokenize($version,"."))+1000*intval($this->Tokenize("."))+intval($this->Tokenize(""));
1191 $protocol_version=($curl_version<713002 ? "1.0" : $this->protocol_version);
1192 }
1193 else
1194 $protocol_version=$this->protocol_version;
1195 $this->request=$this->request_method." ".$request_uri." HTTP/".$protocol_version;
1196 if($body_length
1197 || ($body_length=strlen($this->request_body)))
1198 $this->request_headers["Content-Length"]=$body_length;
1199 for($headers=array(),$host_set=0,Reset($this->request_headers),$header=0;$header<count($this->request_headers);Next($this->request_headers),$header++)
1200 {
1201 $header_name=Key($this->request_headers);
1202 $header_value=$this->request_headers[$header_name];
1203 if(GetType($header_value)=="array")
1204 {
1205 for(Reset($header_value),$value=0;$value<count($header_value);Next($header_value),$value++)
1206 $headers[]=$header_name.": ".$header_value[Key($header_value)];
1207 }
1208 else
1209 $headers[]=$header_name.": ".$header_value;
1210 if(strtolower(Key($this->request_headers))=="host")
1211 {
1212 $this->request_host=strtolower($header_value);
1213 $host_set=1;
1214 }
1215 }
1216 if(!$host_set)
1217 {
1218 $headers[]="Host: ".$this->host_name;
1219 $this->request_host=strtolower($this->host_name);
1220 }
1221 if(count($this->cookies))
1222 {
1223 $cookies=array();
1224 $this->PickCookies($cookies,0);
1225 if(strtolower($this->protocol)=="https")
1226 $this->PickCookies($cookies,1);
1227 if(count($cookies))
1228 {
1229 $h=count($headers);
1230 $headers[$h]="Cookie:";
1231 for(Reset($cookies),$cookie=0;$cookie<count($cookies);Next($cookies),$cookie++)
1232 {
1233 $cookie_name=Key($cookies);
1234 $headers[$h].=" ".$cookie_name."=".$cookies[$cookie_name]["value"].";";
1235 }
1236 }
1237 }
1238 $next_state = "RequestSent";
1239 if($this->use_curl)
1240 {
1241 if(IsSet($arguments['StreamRequest']))
1242 return($this->SetError("Streaming request data is not supported when using Curl"));
1243 if($body_length
1244 && strlen($this->request_body)==0)
1245 {
1246 for($request_body="",$success=1,$part=0;$part<count($post_parts);$part++)
1247 {
1248 $request_body.=$post_parts[$part]["HEADERS"].$post_parts[$part]["DATA"];
1249 if(IsSet($post_parts[$part]["FILENAME"]))
1250 {
1251 if(!($file=@fopen($post_parts[$part]["FILENAME"],"rb")))
1252 {
1253 $this->SetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg);
1254 $success=0;
1255 break;
1256 }
1257 while(!feof($file))
1258 {
1259 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1260 {
1261 $this->SetPHPError("could not read upload file", $php_errormsg);
1262 $success=0;
1263 break;
1264 }
1265 $request_body.=$block;
1266 }
1267 fclose($file);
1268 if(!$success)
1269 break;
1270 }
1271 $request_body.="\r\n";
1272 }
1273 $request_body.="--".$boundary."--\r\n";
1274 }
1275 else
1276 $request_body=$this->request_body;
1277 curl_setopt($this->connection,CURLOPT_HEADER,1);
1278 curl_setopt($this->connection,CURLOPT_RETURNTRANSFER,1);
1279 if($this->timeout)
1280 curl_setopt($this->connection,CURLOPT_TIMEOUT,$this->timeout);
1281 curl_setopt($this->connection,CURLOPT_SSL_VERIFYPEER,0);
1282 curl_setopt($this->connection,CURLOPT_SSL_VERIFYHOST,0);
1283 $request=$this->request."\r\n".implode("\r\n",$headers)."\r\n\r\n".$request_body;
1284 curl_setopt($this->connection,CURLOPT_CUSTOMREQUEST,$request);
1285 if($this->debug)
1286 $this->OutputDebug("C ".$request);
1287 if(!($success=(strlen($this->response=curl_exec($this->connection))!=0)))
1288 {
1289 $error=curl_error($this->connection);
1290 $this->SetError("Could not execute the request".(strlen($error) ? ": ".$error : ""));
1291 }
1292 }
1293 else
1294 {
1295 if(($success=$this->PutLine($this->request)))
1296 {
1297 for($header=0;$header<count($headers);$header++)
1298 {
1299 if(!$success=$this->PutLine($headers[$header]))
1300 break;
1301 }
1302 if($success
1303 && ($success=$this->PutLine("")))
1304 {
1305 if(IsSet($arguments['StreamRequest']))
1306 $next_state = "SendingRequestBody";
1307 elseif($body_length)
1308 {
1309 if(strlen($this->request_body))
1310 $success=$this->PutData($this->request_body);
1311 else
1312 {
1313 for($part=0;$part<count($post_parts);$part++)
1314 {
1315 if(!($success=$this->PutData($post_parts[$part]["HEADERS"]))
1316 || !($success=$this->PutData($post_parts[$part]["DATA"])))
1317 break;
1318 if(IsSet($post_parts[$part]["FILENAME"]))
1319 {
1320 if(!($file=@fopen($post_parts[$part]["FILENAME"],"rb")))
1321 {
1322 $this->SetPHPError("could not open upload file ".$post_parts[$part]["FILENAME"], $php_errormsg);
1323 $success=0;
1324 break;
1325 }
1326 while(!feof($file))
1327 {
1328 if(GetType($block=@fread($file,$this->file_buffer_length))!="string")
1329 {
1330 $this->SetPHPError("could not read upload file", $php_errormsg);
1331 $success=0;
1332 break;
1333 }
1334 if(!($success=$this->PutData($block)))
1335 break;
1336 }
1337 fclose($file);
1338 if(!$success)
1339 break;
1340 }
1341 if(!($success=$this->PutLine("")))
1342 break;
1343 }
1344 if($success)
1345 $success=$this->PutLine("--".$boundary."--");
1346 }
1347 if($success)
1348 $sucess=$this->FlushData();
1349 }
1350 }
1351 }
1352 }
1353 if(!$success)
1354 return($this->SetError("5 could not send the HTTP request: ".$this->error));
1355 $this->state=$next_state;
1356 return("");
1357 }
1358
1359 Function SetCookie($name, $value, $expires="" , $path="/" , $domain="" , $secure=0, $verbatim=0)
1360 {
1361 if(strlen($this->error))
1362 return($this->error);
1363 if(strlen($name)==0)
1364 return($this->SetError("it was not specified a valid cookie name"));
1365 if(strlen($path)==0
1366 || strcmp($path[0],"/"))
1367 return($this->SetError($path." is not a valid path for setting cookie ".$name));
1368 if($domain==""
1369 || !strpos($domain,".",$domain[0]=="." ? 1 : 0))
1370 return($this->SetError($domain." is not a valid domain for setting cookie ".$name));
1371 $domain=strtolower($domain);
1372 if(!strcmp($domain[0],"."))
1373 $domain=substr($domain,1);
1374 if(!$verbatim)
1375 {
1376 $name=$this->CookieEncode($name,1);
1377 $value=$this->CookieEncode($value,0);
1378 }
1379 $secure=intval($secure);
1380 $this->cookies[$secure][$domain][$path][$name]=array(
1381 "name"=>$name,
1382 "value"=>$value,
1383 "domain"=>$domain,
1384 "path"=>$path,
1385 "expires"=>$expires,
1386 "secure"=>$secure
1387 );
1388 return("");
1389 }
1390
1391 Function SendRequestBody($data, $end_of_data)
1392 {
1393 if(strlen($this->error))
1394 return($this->error);
1395 switch($this->state)
1396 {
1397 case "Disconnected":
1398 return($this->SetError("1 connection was not yet established"));
1399 case "Connected":
1400 case "ConnectedToProxy":
1401 return($this->SetError("2 request was not sent"));
1402 case "SendingRequestBody":
1403 break;
1404 case "RequestSent":
1405 return($this->SetError("3 request body was already sent"));
1406 default:
1407 return($this->SetError("4 can not send the request body in the current connection state"));
1408 }
1409 $length = strlen($data);
1410 if($length)
1411 {
1412 $size = dechex($length)."\r\n";
1413 if(!$this->PutData($size)
1414 || !$this->PutData($data))
1415 return($this->error);
1416 }
1417 if($end_of_data)
1418 {
1419 $size = "0\r\n";
1420 if(!$this->PutData($size))
1421 return($this->error);
1422 $this->state = "RequestSent";
1423 }
1424 return("");
1425 }
1426
1427 Function ReadReplyHeadersResponse(&$headers)
1428 {
1429 $headers=array();
1430 if(strlen($this->error))
1431 return($this->error);
1432 switch($this->state)
1433 {
1434 case "Disconnected":
1435 return($this->SetError("1 connection was not yet established"));
1436 case "Connected":
1437 return($this->SetError("2 request was not sent"));
1438 case "ConnectedToProxy":
1439 return($this->SetError("2 connection from the remote server from the proxy was not yet established"));
1440 case "SendingRequestBody":
1441 return($this->SetError("4 request body data was not completely sent"));
1442 case "ConnectSent":
1443 $connect = 1;
1444 break;
1445 case "RequestSent":
1446 $connect = 0;
1447 break;
1448 default:
1449 return($this->SetError("3 can not get request headers in the current connection state"));
1450 }
1451 $this->content_length=$this->read_length=$this->read_response=$this->remaining_chunk=0;
1452 $this->content_length_set=$this->chunked=$this->last_chunk_read=$chunked=0;
1453 $this->connection_close=0;
1454 for($this->response_status="";;)
1455 {
1456 $line=$this->GetLine();
1457 if(GetType($line)!="string")
1458 return($this->SetError("4 could not read request reply: ".$this->error));
1459 if(strlen($this->response_status)==0)
1460 {
1461 if(!eregi($match="^http/[0-9]+\\.[0-9]+[ \t]+([0-9]+)[ \t]*(.*)\$",$line,$matches))
1462 return($this->SetError("3 it was received an unexpected HTTP response status"));
1463 $this->response_status=$matches[1];
1464 $this->response_message=$matches[2];
1465 }
1466 if($line=="")
1467 {
1468 if(strlen($this->response_status)==0)
1469 return($this->SetError("3 it was not received HTTP response status"));
1470 $this->state=($connect ? "GotConnectHeaders" : "GotReplyHeaders");
1471 break;
1472 }
1473 $header_name=strtolower($this->Tokenize($line,":"));
1474 $header_value=Trim(Chop($this->Tokenize("\r\n")));
1475 if(IsSet($headers[$header_name]))
1476 {
1477 if(GetType($headers[$header_name])=="string")
1478 $headers[$header_name]=array($headers[$header_name]);
1479 $headers[$header_name][]=$header_value;
1480 }
1481 else
1482 $headers[$header_name]=$header_value;
1483 if(!$connect)
1484 {
1485 switch($header_name)
1486 {
1487 case "content-length":
1488 $this->content_length=intval($headers[$header_name]);
1489 $this->content_length_set=1;
1490 break;
1491 case "transfer-encoding":
1492 $encoding=$this->Tokenize($header_value,"; \t");
1493 if(!$this->use_curl
1494 && !strcmp($encoding,"chunked"))
1495 $chunked=1;
1496 break;
1497 case "set-cookie":
1498 if($this->support_cookies)
1499 {
1500 if(GetType($headers[$header_name])=="array")
1501 $cookie_headers=$headers[$header_name];
1502 else
1503 $cookie_headers=array($headers[$header_name]);
1504 for($cookie=0;$cookie<count($cookie_headers);$cookie++)
1505 {
1506 $cookie_name=trim($this->Tokenize($cookie_headers[$cookie],"="));
1507 $cookie_value=$this->Tokenize(";");
1508 $domain=$this->request_host;
1509 $path="/";
1510 $expires="";
1511 $secure=0;
1512 while(($name=trim(UrlDecode($this->Tokenize("="))))!="")
1513 {
1514 $value=UrlDecode($this->Tokenize(";"));
1515 switch($name)
1516 {
1517 case "domain":
1518 $domain=$value;
1519 break;
1520 case "path":
1521 $path=$value;
1522 break;
1523 case "expires":
1524 if(ereg("^((Mon|Monday|Tue|Tuesday|Wed|Wednesday|Thu|Thursday|Fri|Friday|Sat|Saturday|Sun|Sunday), )?([0-9]{2})\\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\-([0-9]{2,4}) ([0-9]{2})\\:([0-9]{2})\\:([0-9]{2}) GMT\$",$value,$matches))
1525 {
1526 $year=intval($matches[5]);
1527 if($year<1900)
1528 $year+=($year<70 ? 2000 : 1900);
1529 $expires="$year-".$this->months[$matches[4]]."-".$matches[3]." ".$matches[6].":".$matches[7].":".$matches[8];
1530 }
1531 break;
1532 case "secure":
1533 $secure=1;
1534 break;
1535 }
1536 }
1537 if(strlen($this->SetCookie($cookie_name, $cookie_value, $expires, $path , $domain, $secure, 1)))
1538 $this->error="";
1539 }
1540 }
1541 break;
1542 case "connection":
1543 $this->connection_close=!strcmp(strtolower($header_value),"close");
1544 break;
1545 }
1546 }
1547 }
1548 $this->chunked=$chunked;
1549 if($this->content_length_set)
1550 $this->connection_close=0;
1551 return("");
1552 }
1553
1554 Function Redirect(&$headers)
1555 {
1556 if($this->follow_redirect)
1557 {
1558 if(!IsSet($headers["location"])
1559 || (GetType($headers["location"])!="array"
1560 && strlen($location=$headers["location"])==0)
1561 || (GetType($headers["location"])=="array"
1562 && strlen($location=$headers["location"][0])==0))
1563 return($this->SetError("3 it was received a redirect without location URL"));
1564 if(strcmp($location[0],"/"))
1565 {
1566 $location_arguments=parse_url($location);
1567 if(!IsSet($location_arguments["scheme"]))
1568 $location=((GetType($end=strrpos($this->request_uri,"/"))=="integer" && $end>1) ? substr($this->request_uri,0,$end) : "")."/".$location;
1569 }
1570 if(!strcmp($location[0],"/"))
1571 $location=$this->protocol."://".$this->host_name.($this->host_port ? ":".$this->host_port : "").$location;
1572 $error=$this->GetRequestArguments($location,$arguments);
1573 if(strlen($error))
1574 return($this->SetError("could not process redirect url: ".$error));
1575 $arguments["RequestMethod"]="GET";
1576 if(strlen($error=$this->Close())==0
1577 && strlen($error=$this->Open($arguments))==0
1578 && strlen($error=$this->SendRequest($arguments))==0)
1579 {
1580 $this->redirection_level++;
1581 if($this->redirection_level>$this->redirection_limit)
1582 $error="it was exceeded the limit of request redirections";
1583 else
1584 $error=$this->ReadReplyHeaders($headers);
1585 $this->redirection_level--;
1586 }
1587 if(strlen($error))
1588 return($this->SetError($error));
1589 }
1590 return("");
1591 }
1592
1593 Function Authenticate(&$headers, $proxy, &$proxy_authorization, &$user, &$password, &$realm, &$workstation)
1594 {
1595 if($proxy)
1596 {
1597 $authenticate_header="proxy-authenticate";
1598 $authorization_header="Proxy-Authorization";
1599 $authenticate_status="407";
1600 $authentication_mechanism=$this->proxy_authentication_mechanism;
1601 }
1602 else
1603 {
1604 $authenticate_header="www-authenticate";
1605 $authorization_header="Authorization";
1606 $authenticate_status="401";
1607 $authentication_mechanism=$this->authentication_mechanism;
1608 }
1609 if(IsSet($headers[$authenticate_header]))
1610 {
1611 if(function_exists("class_exists")
1612 && !class_exists("sasl_client_class"))
1613 return($this->SetError("the SASL client class needs to be loaded to be able to authenticate".($proxy ? " with the proxy server" : "")." and access this site"));
1614 if(GetType($headers[$authenticate_header])=="array")
1615 $authenticate=$headers[$authenticate_header];
1616 else
1617 $authenticate=array($headers[$authenticate_header]);
1618 for($response="", $mechanisms=array(),$m=0;$m<count($authenticate);$m++)
1619 {
1620 $mechanism=$this->Tokenize($authenticate[$m]," ");
1621 $response=$this->Tokenize("");
1622 if(strlen($authentication_mechanism))
1623 {
1624 if(!strcmp($authentication_mechanism,$mechanism))
1625 {
1626 $mechanisms[]=$mechanism;
1627 break;
1628 }
1629 }
1630 else
1631 $mechanisms[]=$mechanism;
1632 }
1633 $sasl=new sasl_client_class;
1634 if(IsSet($user))
1635 $sasl->SetCredential("user",$user);
1636 if(IsSet($password))
1637 $sasl->SetCredential("password",$password);
1638 if(IsSet($realm))
1639 $sasl->SetCredential("realm",$realm);
1640 if(IsSet($workstation))
1641 $sasl->SetCredential("workstation",$workstation);
1642 $sasl->SetCredential("uri",$this->request_uri);
1643 $sasl->SetCredential("method",$this->request_method);
1644 $sasl->SetCredential("session",$this->session);
1645 do
1646 {
1647 $status=$sasl->Start($mechanisms,$message,$interactions);
1648 }
1649 while($status==SASL_INTERACT);
1650 switch($status)
1651 {
1652 case SASL_CONTINUE:
1653 break;
1654 case SASL_NOMECH:
1655 return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".(strlen($authentication_mechanism) ? "authentication mechanism ".$authentication_mechanism." may not be used: " : "").$sasl->error));
1656 default:
1657 return($this->SetError("Could not start the SASL ".($proxy ? "proxy " : "")."authentication client: ".$sasl->error));
1658 }
1659 if($proxy >= 0)
1660 {
1661 for(;;)
1662 {
1663 if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length)))
1664 return($error);
1665 if(strlen($body)==0)
1666 break;
1667 }
1668 }
1669 $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : "");
1670 $request_arguments=$this->request_arguments;
1671 $arguments=$request_arguments;
1672 $arguments["Headers"][$authorization_header]=$authorization_value;
1673 if(!$proxy
1674 && strlen($proxy_authorization))
1675 $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization;
1676 if(strlen($error=$this->Close())
1677 || strlen($error=$this->Open($arguments)))
1678 return($this->SetError($error));
1679 $authenticated=0;
1680 if(IsSet($message))
1681 {
1682 if($proxy < 0)
1683 {
1684 if(strlen($error=$this->ConnectFromProxy($arguments, $headers)))
1685 return($this->SetError($error));
1686 }
1687 else
1688 {
1689 if(strlen($error=$this->SendRequest($arguments))
1690 || strlen($error=$this->ReadReplyHeadersResponse($headers)))
1691 return($this->SetError($error));
1692 }
1693 if(!IsSet($headers[$authenticate_header]))
1694 $authenticate=array();
1695 elseif(GetType($headers[$authenticate_header])=="array")
1696 $authenticate=$headers[$authenticate_header];
1697 else
1698 $authenticate=array($headers[$authenticate_header]);
1699 for($mechanism=0;$mechanism<count($authenticate);$mechanism++)
1700 {
1701 if(!strcmp($this->Tokenize($authenticate[$mechanism]," "),$sasl->mechanism))
1702 {
1703 $response=$this->Tokenize("");
1704 break;
1705 }
1706 }
1707 switch($this->response_status)
1708 {
1709 case $authenticate_status:
1710 break;
1711 case "301":
1712 case "302":
1713 case "303":
1714 case "307":
1715 if($proxy >= 0)
1716 return($this->Redirect($headers));
1717 default:
1718 if(intval($this->response_status/100)==2)
1719 {
1720 if($proxy)
1721 $proxy_authorization=$authorization_value;
1722 $authenticated=1;
1723 break;
1724 }
1725 if($proxy
1726 && !strcmp($this->response_status,"401"))
1727 {
1728 $proxy_authorization=$authorization_value;
1729 $authenticated=1;
1730 break;
1731 }
1732 return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message));
1733 }
1734 }
1735 for(;!$authenticated;)
1736 {
1737 do
1738 {
1739 $status=$sasl->Step($response,$message,$interactions);
1740 }
1741 while($status==SASL_INTERACT);
1742 switch($status)
1743 {
1744 case SASL_CONTINUE:
1745 $authorization_value=$sasl->mechanism.(IsSet($message) ? " ".($sasl->encode_response ? base64_encode($message) : $message) : "");
1746 $arguments=$request_arguments;
1747 $arguments["Headers"][$authorization_header]=$authorization_value;
1748 if(!$proxy
1749 && strlen($proxy_authorization))
1750 $arguments["Headers"]["Proxy-Authorization"]=$proxy_authorization;
1751 if($proxy < 0)
1752 {
1753 if(strlen($error=$this->ConnectFromProxy($arguments, $headers)))
1754 return($this->SetError($error));
1755 }
1756 else
1757 {
1758 if(strlen($error=$this->SendRequest($arguments))
1759 || strlen($error=$this->ReadReplyHeadersResponse($headers)))
1760 return($this->SetError($error));
1761 }
1762 switch($this->response_status)
1763 {
1764 case $authenticate_status:
1765 if(GetType($headers[$authenticate_header])=="array")
1766 $authenticate=$headers[$authenticate_header];
1767 else
1768 $authenticate=array($headers[$authenticate_header]);
1769 for($response="",$mechanism=0;$mechanism<count($authenticate);$mechanism++)
1770 {
1771 if(!strcmp($this->Tokenize($authenticate[$mechanism]," "),$sasl->mechanism))
1772 {
1773 $response=$this->Tokenize("");
1774 break;
1775 }
1776 }
1777 if($proxy >= 0)
1778 {
1779 for(;;)
1780 {
1781 if(strlen($error=$this->ReadReplyBody($body,$this->file_buffer_length)))
1782 return($error);
1783 if(strlen($body)==0)
1784 break;
1785 }
1786 }
1787 $this->state="Connected";
1788 break;
1789 case "301":
1790 case "302":
1791 case "303":
1792 case "307":
1793 if($proxy >= 0)
1794 return($this->Redirect($headers));
1795 default:
1796 if(intval($this->response_status/100)==2)
1797 {
1798 if($proxy)
1799 $proxy_authorization=$authorization_value;
1800 $authenticated=1;
1801 break;
1802 }
1803 if($proxy
1804 && !strcmp($this->response_status,"401"))
1805 {
1806 $proxy_authorization=$authorization_value;
1807 $authenticated=1;
1808 break;
1809 }
1810 return($this->SetError(($proxy ? "proxy " : "")."authentication error: ".$this->response_status." ".$this->response_message));
1811 }
1812 break;
1813 default:
1814 return($this->SetError("Could not process the SASL ".($proxy ? "proxy " : "")."authentication step: ".$sasl->error));
1815 }
1816 }
1817 }
1818 return("");
1819 }
1820
1821 Function ReadReplyHeaders(&$headers)
1822 {
1823 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1824 return($error);
1825 $proxy_authorization="";
1826 while(!strcmp($this->response_status, "100"))
1827 {
1828 $this->state="RequestSent";
1829 if(strlen($error=$this->ReadReplyHeadersResponse($headers)))
1830 return($error);
1831 }
1832 switch($this->response_status)
1833 {
1834 case "301":
1835 case "302":
1836 case "303":
1837 case "307":
1838 if(strlen($error=$this->Redirect($headers)))
1839 return($error);
1840 break;
1841 case "407":
1842 if(strlen($error=$this->Authenticate($headers, 1, $proxy_authorization, $this->proxy_request_user, $this->proxy_request_password, $this->proxy_request_realm, $this->proxy_request_workstation)))
1843 return($error);
1844 if(strcmp($this->response_status,"401"))
1845 return("");
1846 case "401":
1847 return($this->Authenticate($headers, 0, $proxy_authorization, $this->request_user, $this->request_password, $this->request_realm, $this->request_workstation));
1848 }
1849 return("");
1850 }
1851
1852 Function ReadReplyBody(&$body,$length)
1853 {
1854 $body="";
1855 if(strlen($this->error))
1856 return($this->error);
1857 switch($this->state)
1858 {
1859 case "Disconnected":
1860 return($this->SetError("1 connection was not yet established"));
1861 case "Connected":
1862 case "ConnectedToProxy":
1863 return($this->SetError("2 request was not sent"));
1864 case "RequestSent":
1865 if(($error=$this->ReadReplyHeaders($headers))!="")
1866 return($error);
1867 break;
1868 case "GotReplyHeaders":
1869 break;
1870 default:
1871 return($this->SetError("3 can not get request headers in the current connection state"));
1872 }
1873 if($this->content_length_set)
1874 $length=min($this->content_length-$this->read_length,$length);
1875 if($length>0
1876 && !$this->EndOfInput()
1877 && ($body=$this->ReadBytes($length))=="")
1878 {
1879 if(strlen($this->error))
1880 return($this->SetError("4 could not get the request reply body: ".$this->error));
1881 }
1882 $this->read_length+=strlen($body);
1883 return("");
1884 }
1885
1886 Function SaveCookies(&$cookies, $domain='', $secure_only=0, $persistent_only=0)
1887 {
1888 $now=gmdate("Y-m-d H-i-s");
1889 $cookies=array();
1890 for($secure_cookies=0,Reset($this->cookies);$secure_cookies<count($this->cookies);Next($this->cookies),$secure_cookies++)
1891 {
1892 $secure=Key($this->cookies);
1893 if(!$secure_only
1894 || $secure)
1895 {
1896 for($cookie_domain=0,Reset($this->cookies[$secure]);$cookie_domain<count($this->cookies[$secure]);Next($this->cookies[$secure]),$cookie_domain++)
1897 {
1898 $domain_pattern=Key($this->cookies[$secure]);
1899 $match=strlen($domain)-strlen($domain_pattern);
1900 if(strlen($domain)==0
1901 || ($match>=0
1902 && !strcmp($domain_pattern,substr($domain,$match))
1903 && ($match==0
1904 || $domain_pattern[0]=="."
1905 || $domain[$match-1]==".")))
1906 {
1907 for(Reset($this->cookies[$secure][$domain_pattern]),$path_part=0;$path_part<count($this->cookies[$secure][$domain_pattern]);Next($this->cookies[$secure][$domain_pattern]),$path_part++)
1908 {
1909 $path=Key($this->cookies[$secure][$domain_pattern]);
1910 for(Reset($this->cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<count($this->cookies[$secure][$domain_pattern][$path]);Next($this->cookies[$secure][$domain_pattern][$path]),$cookie++)
1911 {
1912 $cookie_name=Key($this->cookies[$secure][$domain_pattern][$path]);
1913 $expires=$this->cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
1914 if((!$persistent_only
1915 && strlen($expires)==0)
1916 || (strlen($expires)
1917 && strcmp($now,$expires)<0))
1918 $cookies[$secure][$domain_pattern][$path][$cookie_name]=$this->cookies[$secure][$domain_pattern][$path][$cookie_name];
1919 }
1920 }
1921 }
1922 }
1923 }
1924 }
1925 }
1926
1927 Function SavePersistentCookies(&$cookies, $domain='', $secure_only=0)
1928 {
1929 $this->SaveCookies($cookies, $domain, $secure_only, 1);
1930 }
1931
1932 Function GetPersistentCookies(&$cookies, $domain='', $secure_only=0)
1933 {
1934 $this->SavePersistentCookies($cookies, $domain, $secure_only);
1935 }
1936
1937 Function RestoreCookies($cookies, $clear=1)
1938 {
1939 $new_cookies=($clear ? array() : $this->cookies);
1940 for($secure_cookies=0, Reset($cookies); $secure_cookies<count($cookies); Next($cookies), $secure_cookies++)
1941 {
1942 $secure=Key($cookies);
1943 if(GetType($secure)!="integer")
1944 return($this->SetError("invalid cookie secure value type (".serialize($secure).")"));
1945 for($cookie_domain=0,Reset($cookies[$secure]);$cookie_domain<count($cookies[$secure]);Next($cookies[$secure]),$cookie_domain++)
1946 {
1947 $domain_pattern=Key($cookies[$secure]);
1948 if(GetType($domain_pattern)!="string")
1949 return($this->SetError("invalid cookie domain value type (".serialize($domain_pattern).")"));
1950 for(Reset($cookies[$secure][$domain_pattern]),$path_part=0;$path_part<count($cookies[$secure][$domain_pattern]);Next($cookies[$secure][$domain_pattern]),$path_part++)
1951 {
1952 $path=Key($cookies[$secure][$domain_pattern]);
1953 if(GetType($path)!="string"
1954 || strcmp(substr($path, 0, 1), "/"))
1955 return($this->SetError("invalid cookie path value type (".serialize($path).")"));
1956 for(Reset($cookies[$secure][$domain_pattern][$path]),$cookie=0;$cookie<count($cookies[$secure][$domain_pattern][$path]);Next($cookies[$secure][$domain_pattern][$path]),$cookie++)
1957 {
1958 $cookie_name=Key($cookies[$secure][$domain_pattern][$path]);
1959 $expires=$cookies[$secure][$domain_pattern][$path][$cookie_name]["expires"];
1960 $value=$cookies[$secure][$domain_pattern][$path][$cookie_name]["value"];
1961 if(GetType($expires)!="string"
1962 || (strlen($expires)
1963 && !ereg("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\$", $expires)))
1964 return($this->SetError("invalid cookie expiry value type (".serialize($expires).")"));
1965 $new_cookies[$secure][$domain_pattern][$path][$cookie_name]=array(
1966 "name"=>$cookie_name,
1967 "value"=>$value,
1968 "domain"=>$domain_pattern,
1969 "path"=>$path,
1970 "expires"=>$expires,
1971 "secure"=>$secure
1972 );
1973 }
1974 }
1975 }
1976 }
1977 $this->cookies=$new_cookies;
1978 return("");
1979 }
1980};
1981
1982?> \ No newline at end of file
diff --git a/includes/index.php b/includes/index.php new file mode 100644 index 0000000..e6b9bab --- /dev/null +++ b/includes/index.php
@@ -0,0 +1,5 @@
1<?php
2
3header('Location: /index.php');
4
5?>
diff --git a/includes/phpsvnclient.php b/includes/phpsvnclient.php new file mode 100644 index 0000000..811c86d --- /dev/null +++ b/includes/phpsvnclient.php
@@ -0,0 +1,420 @@
1<?php
2/*
3***************************************************************************
4* Copyright (C) 2007-2008 by Sixdegrees *
5* cesar@sixdegrees.com.br *
6* "Working with freedom" *
7* http://www.sixdegrees.com.br *
8* *
9* Permission is hereby granted, free of charge, to any person obtaining *
10* a copy of this software and associated documentation files (the *
11* "Software"), to deal in the Software without restriction, including *
12* without limitation the rights to use, copy, modify, merge, publish, *
13* distribute, sublicense, and/or sell copies of the Software, and to *
14* permit persons to whom the Software is furnished to do so, subject to *
15* the following conditions: *
16* *
17* The above copyright notice and this permission notice shall be *
18* included in all copies or substantial portions of the Software. *
19* *
20* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
21* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
22* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
23* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
24* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
25* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
26* OTHER DEALINGS IN THE SOFTWARE. *
27***************************************************************************
28*/
29define("PHPSVN_DIR",dirname(__FILE__) );
30
31require(PHPSVN_DIR."/http.php");
32require(PHPSVN_DIR."/xml_parser.php");
33require(PHPSVN_DIR."/definitions.php");
34
35
36/**
37 * PHP SVN CLIENT
38 *
39 * This class is a SVN client. It can perform read operations
40 * to a SVN server (over Web-DAV).
41 * It can get directory files, file contents, logs. All the operaration
42 * could be done for a specific version or for the last version.
43 *
44 * @author Cesar D. Rodas <cesar@sixdegrees.com.br>
45 * @license BSD License
46 */
47class phpSVNclient
48{
49 /**
50 * SVN Repository URL
51 *
52 * @var string
53 * @access private
54 */
55 var $_url;
56 /**
57 * Cache, for don't request the same thing in a
58 * short period of time.
59 *
60 * @var string
61 * @access private
62 */
63 var $_cache;
64 /**
65 * HTTP Client object
66 *
67 * @var object
68 * @access private
69 */
70 var $_http;
71 /**
72 * Respository Version.
73 *
74 * @access private
75 * @var interger
76 */
77 var $_repVersion;
78 /**
79 * Password
80 *
81 * @access private
82 * @var string
83 */
84 var $pass;
85 /**
86 * Password
87 *
88 * @access private
89 * @var string
90 */
91 var $user;
92 /**
93 * Last error number
94 *
95 * Possible values are NOT_ERROR, NOT_FOUND, AUTH_REQUIRED, UNKOWN_ERROR
96 *
97 * @access public
98 * @var integer
99 */
100 var $errNro;
101
102 function phpSVNclient()
103 {
104 $http = & $this->_http;
105 $http = new http_class;
106 $http->user_agent = "phpSVNclient (http://cesars.users.phpclasses.org/svnclient)";
107 }
108
109 /**
110 * Set URL
111 *
112 * Set the project repository URL.
113 *
114 * @param string $url URL of the project.
115 * @access public
116 */
117 function setRespository($url)
118 {
119 $this->_url = $url;
120 }
121
122 /**
123 * Add Authentication settings
124 *
125 * @param string $user Username
126 * @param string $pass Password
127 */
128 function setAuth($user,$pass) {
129 $this->user = $user;
130 $this->pass = $pass;
131 }
132
133 /**
134 * Get Files
135 *
136 * This method returns all the files in $folder
137 * in the version $version of the repository.
138 *
139 * @param string $folder Folder to get files
140 * @param integer $version Repository version, -1 means actual
141 * @return array List of files.
142 */
143 function getDirectoryFiles($folder='/',$version=-1) {
144 $actVersion = $this->getVersion();
145 if ( $version == -1 || $version > $actVersion) {
146 $version = $actVersion;
147 }
148 $url = $this->cleanURL($this->_url."/!svn/bc/".$version."/".$folder."/");
149 $this->initQuery($args,"PROPFIND",$url);
150 $args['Body'] = PHPSVN_NORMAL_REQUEST;
151 $args['Headers']['Content-Length'] = strlen(PHPSVN_NORMAL_REQUEST);
152
153 if ( ! $this->Request($args, $headers, $body) ) {
154 return false;
155 }
156 $parser=new xml_parser_class;
157 $parser->Parse( $body,true);
158
159
160 $fileinfo = array(
161
162 SVN_LAST_MODIFIED => "last-mod",
163 SVN_RELATIVE_URL => "path",
164 SVN_STATUS => "status"
165 );
166
167 $start = false;
168 $last = "";
169 $tmp = array();
170 $files = array();
171 $tmp1 = 0;
172
173
174 foreach($parser->structure as $key=>$value) {
175 if ( is_array($value) and $value["Tag"] == SVN_FILE) {
176 if ( count($tmp) > 0 && $tmp1++ > 0) {
177 $files[] = $tmp;
178 }
179 $start=true;
180 $last = "";
181 $tmp = array();
182 continue;
183 }
184 if (!$start) continue;
185 if ( $last != "") {
186 $tmp[ $fileinfo[$last] ] = $value;
187 $last = "";
188 continue;
189 }
190 if ( is_array($value) && isset($value["Tag"]) && isset( $fileinfo[$value["Tag"]] ) ) {
191 $last = $value["Tag"];
192 }
193 }
194
195 return $files;
196 }
197
198 /**
199 * Returns file contents
200 *
201 * @param string $file File pathname
202 * @param integer $version File Version
203 * @return Array File content and information
204 */
205 function getFile($file,$version=-1) {
206 $actVersion = $this->getVersion();
207 if ( $version == -1 || $version > $actVersion) {
208 $version = $actVersion;
209 }
210 $url = $this->cleanURL($this->_url."/!svn/bc/".$version."/".$file."/");
211 $this->initQuery($args,"GET",$url);
212 if ( ! $this->Request($args, $headers, $body) )
213 return false;
214 return $body;
215 }
216
217 /**
218 * Get changes logs of a file.
219 *
220 * Get repository change logs between version
221 * $vini and $vend.
222 *
223 * @param integer $vini Initial Version
224 * @param integer $vend End Version
225 * @return Array Respository Logs
226 */
227 function getRepositoryLogs($vini=0,$vend=-1) {
228 return $this->getFileLogs("/",$vini,$vend);
229 }
230
231 /**
232 * Get changes logs of a file.
233 *
234 * Get repository change of a file between version
235 * $vini and $vend.
236 *
237 * @param
238 * @param integer $vini Initial Version
239 * @param integer $vend End Version
240 * @return Array Respository Logs
241 */
242 function getFileLogs($file, $vini=0,$vend=-1) {
243 $actVersion = $this->getVersion();
244 if ( $vend == -1 || $vend > $actVersion)
245 $vend = $actVersion;
246 else
247 $vend++;
248
249 if ( $vini < 0) $vini=0;
250 if ( $vini > $vend) $vini = $vend;
251
252 $url = $this->cleanURL($this->_url."/!svn/bc/".$actVersion."/".$file."/");
253 $this->initQuery($args,"REPORT",$url);
254 $args['Body'] = sprintf(PHPSVN_LOGS_REQUEST,$vini,$vend);
255 $args['Headers']['Content-Length'] = strlen($args['Body']);
256 $args['Headers']['Depth']=1;
257
258 if ( ! $this->Request($args, $headers, $body) )
259 return false;
260
261
262 $parser=new xml_parser_class;
263 $parser->Parse( $body,true);
264
265
266 $fileinfo = array(
267 SVN_LOGS_VERSION=>"version",
268 SVN_LOGS_AUTHOR => "author",
269 SVN_LOGS_DATE => "date",
270 SVN_LOGS_COMMENT => "comment"
271
272 );
273
274 $start = false;
275 $last = "";
276 $tmp = array();
277 $files = array();
278 $tmp1 = 0;
279
280
281 foreach($parser->structure as $key=>$value) {
282 if ( is_array($value) and $value["Tag"] == SVN_LOGS_BEGINGS) {
283 if ( count($tmp) > 0 && $tmp1++ > 0) {
284 $logs[] = $tmp;
285 }
286 $start=true;
287 $last = "";
288 $tmp = array();
289 continue;
290 }
291 if (!$start) continue;
292 if ( $last != "") {
293 $tmp[ $fileinfo[$last] ] = $value;
294 $last = "";
295 continue;
296 }
297 if ( is_array($value) && isset($value["Tag"]) && isset( $fileinfo[$value["Tag"]] ) ) {
298 $last = $value["Tag"];
299 }
300 }
301
302 return $logs;
303 }
304
305 /**
306 * Get the repository version
307 *
308 * @return integer Repository version
309 * @access public
310 */
311 function getVersion() {
312 if ( $this->_repVersion < 0) return $this->_repVersion;
313
314 $this->_repVersion = -1;
315
316 $this->initQuery($args,"PROPFIND",$this->cleanURL($this->_url."/!svn/vcc/default") );
317 $args['Body'] = PHPSVN_VERSION_REQUEST;
318 $args['Headers']['Content-Length'] = strlen(PHPSVN_NORMAL_REQUEST);
319 $args['Headers']['Depth']=0;
320
321 if ( !$this->Request($args, $tmp, $body) ) {
322 return $this->_repVersion;
323 }
324
325 $parser=new xml_parser_class;
326 $parser->Parse( $body,true);
327 $enable=false;
328 foreach($parser->structure as $value) {
329 if ( $enable ) {
330 $t = explode("/",$value);
331 if ( is_numeric($t[ count($t) -1 ]) ) {
332 $this->_repVersion = $t[ count($t) -1 ];
333 break;
334 }
335 }
336 if ( is_array($value) && $value['Tag'] == 'D:href') $enable = true;
337 }
338
339 return $this->_repVersion;
340 }
341
342 /**
343 * Prepare HTTP CLIENT object
344 *
345 * @param array &$arguments Byreferences variable.
346 * @param string $method Method for the request (GET,POST,PROPFIND, REPORT,ETC).
347 * @param string $url URL for the action.
348 * @access private
349 */
350 function initQuery(&$arguments,$method, $url) {
351 $http = & $this->_http;
352 $http->GetRequestArguments($url,$arguments);
353 if ( isset($this->user) && isset($this->pass)) {
354 $arguments["Headers"]["Authorization"] = " Basic ".base64_encode($this->user.":".$this->pass);
355 }
356 $arguments["RequestMethod"]=$method;
357 $arguments["Headers"]["Content-Type"] = "text/xml";
358 $arguments["Headers"]["Depth"] = 1;
359 }
360
361 /**
362 * Open a connection, send request, read header
363 * and body.
364 *
365 * @param Array $args Connetion's argument
366 * @param Array &$headers Array with the header response.
367 * @param string &$body Body response.
368 * @return boolean True is query success
369 * @access private
370 */
371 function Request($args, &$headers, &$body) {
372 $http = & $this->_http;
373 $http->Open($args);
374 $http->SendRequest($args);
375 $http->ReadReplyHeaders($headers);
376 if ($http->response_status[0] != 2) {
377 switch( $http->response_status ) {
378 case 404:
379 $this->errNro=NOT_FOUND;
380 break;
381 case 401:
382 $this->errNro=AUTH_REQUIRED;
383 break;
384 default:
385 $this->errNro=UNKNOWN_ERROR;
386
387 }
388 $http->close();
389 return false;
390 }
391 $this->errNro = NO_ERROR;
392 $body='';
393 $tbody='';
394 for(;;)
395 {
396 $error=$http->ReadReplyBody($tbody,1000);
397 if($error!="" || strlen($tbody)==0) break;
398 $body.=($tbody);
399 }
400 $http->close();
401 return true;
402 }
403
404 /**
405 * Clean URL
406 *
407 * Delete "//" on URL requests.
408 *
409 * @param string $url URL
410 * @return string New cleaned URL.
411 * @access private
412 */
413 function cleanURL($url) {
414 $t = parse_url($url);
415 if ( isset($t['path']) )
416 $t['path'] = str_replace("//","/",$t['path']);
417 return $t['scheme']."://".$t['host'].(isset($t['path']) ? $t['path'] : "/");
418 }
419}
420?>
diff --git a/includes/recaptchalib.php b/includes/recaptchalib.php new file mode 100644 index 0000000..897c509 --- /dev/null +++ b/includes/recaptchalib.php
@@ -0,0 +1,277 @@
1<?php
2/*
3 * This is a PHP library that handles calling reCAPTCHA.
4 * - Documentation and latest version
5 * http://recaptcha.net/plugins/php/
6 * - Get a reCAPTCHA API Key
7 * http://recaptcha.net/api/getkey
8 * - Discussion group
9 * http://groups.google.com/group/recaptcha
10 *
11 * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
12 * AUTHORS:
13 * Mike Crawford
14 * Ben Maurer
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining a copy
17 * of this software and associated documentation files (the "Software"), to deal
18 * in the Software without restriction, including without limitation the rights
19 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 * copies of the Software, and to permit persons to whom the Software is
21 * furnished to do so, subject to the following conditions:
22 *
23 * The above copyright notice and this permission notice shall be included in
24 * all copies or substantial portions of the Software.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 * THE SOFTWARE.
33 */
34
35/**
36 * The reCAPTCHA server URL's
37 */
38define("RECAPTCHA_API_SERVER", "http://api.recaptcha.net");
39define("RECAPTCHA_API_SECURE_SERVER", "https://api-secure.recaptcha.net");
40define("RECAPTCHA_VERIFY_SERVER", "api-verify.recaptcha.net");
41
42/**
43 * Encodes the given data into a query string format
44 * @param $data - array of string elements to be encoded
45 * @return string - encoded request
46 */
47function _recaptcha_qsencode ($data) {
48 $req = "";
49 foreach ( $data as $key => $value )
50 $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
51
52 // Cut the last '&'
53 $req=substr($req,0,strlen($req)-1);
54 return $req;
55}
56
57
58
59/**
60 * Submits an HTTP POST to a reCAPTCHA server
61 * @param string $host
62 * @param string $path
63 * @param array $data
64 * @param int port
65 * @return array response
66 */
67function _recaptcha_http_post($host, $path, $data, $port = 80) {
68
69 $req = _recaptcha_qsencode ($data);
70
71 $http_request = "POST $path HTTP/1.0\r\n";
72 $http_request .= "Host: $host\r\n";
73 $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
74 $http_request .= "Content-Length: " . strlen($req) . "\r\n";
75 $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
76 $http_request .= "\r\n";
77 $http_request .= $req;
78
79 $response = '';
80 if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
81 die ('Could not open socket');
82 }
83
84 fwrite($fs, $http_request);
85
86 while ( !feof($fs) )
87 $response .= fgets($fs, 1160); // One TCP-IP packet
88 fclose($fs);
89 $response = explode("\r\n\r\n", $response, 2);
90
91 return $response;
92}
93
94
95
96/**
97 * Gets the challenge HTML (javascript and non-javascript version).
98 * This is called from the browser, and the resulting reCAPTCHA HTML widget
99 * is embedded within the HTML form it was called from.
100 * @param string $pubkey A public key for reCAPTCHA
101 * @param string $error The error given by reCAPTCHA (optional, default is null)
102 * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
103
104 * @return string - The HTML to be embedded in the user's form.
105 */
106function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
107{
108 if ($pubkey == null || $pubkey == '') {
109 die ("To use reCAPTCHA you must get an API key from <a href='http://recaptcha.net/api/getkey'>http://recaptcha.net/api/getkey</a>");
110 }
111
112 if ($use_ssl) {
113 $server = RECAPTCHA_API_SECURE_SERVER;
114 } else {
115 $server = RECAPTCHA_API_SERVER;
116 }
117
118 $errorpart = "";
119 if ($error) {
120 $errorpart = "&amp;error=" . $error;
121 }
122 return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
123
124 <noscript>
125 <iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
126 <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
127 <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
128 </noscript>';
129}
130
131
132
133
134/**
135 * A ReCaptchaResponse is returned from recaptcha_check_answer()
136 */
137class ReCaptchaResponse {
138 var $is_valid;
139 var $error;
140}
141
142
143/**
144 * Calls an HTTP POST function to verify if the user's guess was correct
145 * @param string $privkey
146 * @param string $remoteip
147 * @param string $challenge
148 * @param string $response
149 * @param array $extra_params an array of extra variables to post to the server
150 * @return ReCaptchaResponse
151 */
152function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
153{
154 if ($privkey == null || $privkey == '') {
155 die ("To use reCAPTCHA you must get an API key from <a href='http://recaptcha.net/api/getkey'>http://recaptcha.net/api/getkey</a>");
156 }
157
158 if ($remoteip == null || $remoteip == '') {
159 die ("For security reasons, you must pass the remote ip to reCAPTCHA");
160 }
161
162
163
164 //discard spam submissions
165 if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
166 $recaptcha_response = new ReCaptchaResponse();
167 $recaptcha_response->is_valid = false;
168 $recaptcha_response->error = 'incorrect-captcha-sol';
169 return $recaptcha_response;
170 }
171
172 $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/verify",
173 array (
174 'privatekey' => $privkey,
175 'remoteip' => $remoteip,
176 'challenge' => $challenge,
177 'response' => $response
178 ) + $extra_params
179 );
180
181 $answers = explode ("\n", $response [1]);
182 $recaptcha_response = new ReCaptchaResponse();
183
184 if (trim ($answers [0]) == 'true') {
185 $recaptcha_response->is_valid = true;
186 }
187 else {
188 $recaptcha_response->is_valid = false;
189 $recaptcha_response->error = $answers [1];
190 }
191 return $recaptcha_response;
192
193}
194
195/**
196 * gets a URL where the user can sign up for reCAPTCHA. If your application
197 * has a configuration page where you enter a key, you should provide a link
198 * using this function.
199 * @param string $domain The domain where the page is hosted
200 * @param string $appname The name of your application
201 */
202function recaptcha_get_signup_url ($domain = null, $appname = null) {
203 return "http://recaptcha.net/api/getkey?" . _recaptcha_qsencode (array ('domain' => $domain, 'app' => $appname));
204}
205
206function _recaptcha_aes_pad($val) {
207 $block_size = 16;
208 $numpad = $block_size - (strlen ($val) % $block_size);
209 return str_pad($val, strlen ($val) + $numpad, chr($numpad));
210}
211
212/* Mailhide related code */
213
214function _recaptcha_aes_encrypt($val,$ky) {
215 if (! function_exists ("mcrypt_encrypt")) {
216 die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
217 }
218 $mode=MCRYPT_MODE_CBC;
219 $enc=MCRYPT_RIJNDAEL_128;
220 $val=_recaptcha_aes_pad($val);
221 return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
222}
223
224
225function _recaptcha_mailhide_urlbase64 ($x) {
226 return strtr(base64_encode ($x), '+/', '-_');
227}
228
229/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
230function recaptcha_mailhide_url($pubkey, $privkey, $email) {
231 if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
232 die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
233 "you can do so at <a href='http://mailhide.recaptcha.net/apikey'>http://mailhide.recaptcha.net/apikey</a>");
234 }
235
236
237 $ky = pack('H*', $privkey);
238 $cryptmail = _recaptcha_aes_encrypt ($email, $ky);
239
240 return "http://mailhide.recaptcha.net/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
241}
242
243/**
244 * gets the parts of the email to expose to the user.
245 * eg, given johndoe@example,com return ["john", "example.com"].
246 * the email is then displayed as john...@example.com
247 */
248function _recaptcha_mailhide_email_parts ($email) {
249 $arr = preg_split("/@/", $email );
250
251 if (strlen ($arr[0]) <= 4) {
252 $arr[0] = substr ($arr[0], 0, 1);
253 } else if (strlen ($arr[0]) <= 6) {
254 $arr[0] = substr ($arr[0], 0, 3);
255 } else {
256 $arr[0] = substr ($arr[0], 0, 4);
257 }
258 return $arr;
259}
260
261/**
262 * Gets html to display an email address given a public an private key.
263 * to get a key, go to:
264 *
265 * http://mailhide.recaptcha.net/apikey
266 */
267function recaptcha_mailhide_html($pubkey, $privkey, $email) {
268 $emailparts = _recaptcha_mailhide_email_parts ($email);
269 $url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
270
271 return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) .
272 "' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
273
274}
275
276
277?>
diff --git a/includes/session.php b/includes/session.php new file mode 100644 index 0000000..ff47e12 --- /dev/null +++ b/includes/session.php
@@ -0,0 +1,61 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/session.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25session_start();
26
27function sess_exists($name)
28{
29 return(isset($_SESSION[$name]));
30}
31
32function sess_set($name,$value)
33{
34 $_SESSION[$name] = $value;
35}
36
37function sess_get($name)
38{
39 return $_SESSION[$name];
40}
41
42
43function sess_getifset($name)
44{
45 if (sess_exists($name))
46 {
47 return sess_get($name);
48 } else {
49 return false;
50 }
51}
52
53function sess_delete($name)
54{
55 if (sess_exists($name))
56 {
57 unset($_SESSION[$name]);
58 }
59}
60
61?>
diff --git a/includes/specialdates.php b/includes/specialdates.php new file mode 100755 index 0000000..91a356e --- /dev/null +++ b/includes/specialdates.php
@@ -0,0 +1,314 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/specialdates.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25/* REMEMBER!
26Months: Jan=1-Dec=12
27Dates: 1=1-...=...
28Days: Mon=1-Sun=7 */
29
30$specialdates = array();
31
32$j=0;
33for ($j=0;$j<(365+sd_isLeapYear());$j++)
34{
35 $specialdates[$j] = '';
36}
37
38sd_solar_annual('New Years Day',1,1); //BG Pic; DateFinder
39sd_lunar_annual('Martin Luther King Day',1,1,3); //BG Pic; DateFinder
40sd_solar_annual('Groundhog Day',2,2); //BG Pic; DateFinder
41sd_lunar_annual('Presidents Day',2,1,3); //DateFinder
42sd_solar_annual('Valentines Day',2,14); //BG Pic; DateFinder
43sd_solar_multiannual('Leap Day',2,29,4,2004); //DateFinder
44sd_solar_annual('St Patricks Day',3,17); //DateFinder
45sd_solar_annual('Tris CIEday',4,22); //DateFinder
46sd_solar_annual('Silence Day',4,25); //DateFinder
47sd_solar_annual('WCA Day',5,5); //DateFinder
48sd_lunar_annual('Mothers Day',5,7,2); //BG Pic; DateFinder; Header Pic
49sd_lunar_annual('Memorial Day',5,1,4); //BG Pic; DateFinder; Header Pic
50sd_easter(); //BG Pic; DateFinder
51sd_solar_annual('Hatkirbys B-Day',6,7); //BG Pic; DateFinder; Header Pic
52sd_solar_annual('Flag Day',6,14); //BG Pic; DateFinder
53sd_lunar_annual('Fathers Day',6,7,3); //BG Pic; DateFinder
54sd_solar_annual('CTNH',6,17); //Header Pic
55sd_solar_annual('Independance Day',7,4); //BG Pic; DateFinder
56sd_lunar_annual('SysAdminDay',7,5,4); //DateFinder
57sd_lunar_annual('Labor Day',9,1,1); //Yet to be implemented
58sd_solar_annual('Four Island A',9,22); //BG Pic; DateFinder; Header Pic
59sd_lunar_annual('Columbus Day',10,1,2); //BG Pic; DateFinder
60sd_solar_annual('Halloween',10,31); //BG Pic; DateFinder
61sd_solar_annual('Veterans Day',11,11); //BG Pic; DateFinder
62sd_lunar_annual('Thanksgiving',11,4,4); //DateFinder
63sd_kirbyWeek(); //BG Pic; DateFinder
64sd_solar_annual('Christmas Eve',12,24); //BG Pic; DateFinder
65sd_solar_annual('Christmas Day',12,25); //BG Pic; DateFinder
66sd_solar_annual('New Years Eve',12,31); //BG Pic; DateFinder
67
68function sd_solar_annual($id,$month,$date)
69{
70 $did = sd_getMonthStart($month-1);
71 $did += ($date-1);
72 sd_addDateIn($id,$did);
73}
74
75function sd_solar_monthly($id,$date)
76{
77 $i=0;
78 for ($i=0;$i<12;$i++)
79 {
80 sd_solar_annual($id,$i+1,$date);
81 }
82}
83
84function sd_lunar_annual($id,$month,$dotw,$wn)
85{
86 $did = sd_getMonthStart($month-1);
87 $ys = sd_clearDate();
88 $ys+=(60*60*24*$did);
89 if (sd_date('N',$ys)>$dotw)
90 {
91 $ys+=(((7-sd_date('N',$ys))+$dotw)*60*60*24);
92 } else {
93 $ys+=(($dotw-sd_date('N',$ys))*60*60*24);
94 }
95 $ys+=(($wn-1)*60*60*24*7);
96 $did = sd_getMonthStart(sd_date('m',$ys)-1);
97 $did += (sd_date('j',$ys)-1);
98 sd_addDateIn($id,$did);
99}
100
101function sd_getMonthStart($month)
102{
103 if ($month==0)
104 {
105 return 0;
106 } else {
107 $c = sd_daysInMonth($month-1)+sd_getMonthStart($month-1);
108 if (date('F jS Y',sd_clearDate()+($c*60*60*24)) == 'March 31st 2008' || $month == 2)
109 {
110// echo($month . ':' . sd_daysInMonth($month-1) . ':' . sd_getMonthStart($month-1) . "\n");
111 }
112 return ($c);
113 }
114}
115
116function sd_daysInMonth($month)
117{
118 switch ($month)
119 {
120 case 0: return 31;
121 case 1: return (28+sd_isLeapYear());
122 case 2: return 31;
123 case 3: return 30;
124 case 4: return 31;
125 case 5: return 30;
126 case 6: return 31;
127 case 7: return 31;
128 case 8: return 30;
129 case 9: return 31;
130 case 10: return 30;
131 case 11: return 31;
132 default: throw new Exception('Invalid month ID');
133 }
134}
135
136function sd_isLeapYear()
137{
138 return sd_date('L');
139}
140
141function sd_isSpecialDay($id)
142{
143 global $specialdates;
144 $did = sd_getMonthStart(sd_date('n')-1);
145 $did += (sd_date('j')-1);
146 if ($specialdates[$did] == $id)
147 {
148 return 1;
149 } else {
150 return 0;
151 }
152}
153
154function sd_ifNoSpecialDay()
155{
156 global $specialdates;
157 $did = sd_getMonthStart(sd_date('n')-1);
158 $did += (sd_date('j')-1);
159 if ($specialdates[$did] == '')
160 {
161 return 1;
162 } else {
163 return 0;
164 }
165}
166
167function sd_kirbyWeek()
168{
169 $did = sd_getMonthStart(11);
170 $ys = sd_clearDate();
171 $ys+=(60*60*24*24);
172 $ys+=(60*60*24*$did);
173 $tWD=sd_date('N',$ys);
174 if ($tWD==7) {$tWD=0;}
175 $ys-=($tWD*60*60*24);
176 $ys-=(60*60*24*7);
177 sd_solar_annual('Kirby Week',12,sd_date('j',$ys));
178 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+1);
179 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+2);
180 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+3);
181 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+4);
182 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+5);
183 sd_solar_annual('Kirby Week',12,sd_date('j',$ys)+6);
184}
185
186function sd_easter()
187{
188 $nYear = sd_date('Y');
189 $nCent = $nYear/100;
190 $nRemain19 = $nYear%19;
191 $n1 = ($nCent-15)/2+202-11*$nRemain19;
192 if ($nCent>20)
193 {
194 if ($nCent>26) {$n1--;}
195 if ($nCent>38) {$n1--;}
196 if ($nCent==21 || $nCent==24 || $nCent==25 || $nCent==33 || $nCent==36 || $nCent==37) {$n1--;}
197 }
198 $n1%=30;
199 if ($n1==29 || ($n1 && $nRemain19>10)) {$n1--;}
200 $ys = sd_clearDate();
201 if ($n1>10)
202 {
203 $ys+=(60*60*24*sd_getMonthStart(3));
204 $ys+=(60*60*24*($n1-10-1));
205 } else {
206 $ys+=(60*60*24*sd_getMonthStart(2));
207 $ys+=(60*60*24*($n1+21-1));
208 }
209 $nWD = sd_date('N',$ys);
210 if ($nWD==7) {$nWD=0;}
211 $ys+=(60*60*24*(7-$nWD));
212 sd_solar_annual('Easter',sd_date('m',$ys),sd_date('j',$ys));
213 $ys-=(60*60*24*46);
214 sd_solar_annual('Mardi Gras',sd_date('m',$ys),sd_date('j',$ys));
215 $ys+=(60*60*24);
216 sd_solar_annual('Ash Wednesday',sd_date('m',$ys),sd_date('j',$ys));
217 $ys+=(60*60*24*38);
218 sd_solar_annual('Palm Sunday',sd_date('m',$ys),sd_date('j',$ys));
219 $ys+=(60*60*24*5);
220 sd_solar_annual('Good Friday',sd_date('m',$ys),sd_date('j',$ys));
221}
222
223function sd_findDay($id)
224{
225 global $specialdates;
226 $i=0;
227 for ($i=0;$i<(365+sd_isLeapYear());$i++)
228 {
229 if ($specialdates[$i] == $id)
230 {
231 return $i;
232 }
233 }
234 throw new Exception('Specified holiday does not exist');
235}
236
237function sd_findNextDay()
238{
239 global $specialdates;
240 $did = sd_getMonthStart(sd_date('n')-1);
241 $did += (sd_date('j')-1);
242 $i=0;
243 for ($i=$did;$i<(365+sd_isLeapYear());$i++)
244 {
245 if ($specialdates[$i] != '')
246 {
247 return $i;
248 }
249 }
250 throw new Exception('No more holidays this year');
251}
252
253function sd_getCurrentDay()
254{
255 global $specialdates;
256 $did = sd_getMonthStart(sd_date('n')-1);
257 $did += (sd_date('j')-1);
258 return $specialdates[$did];
259}
260
261function sd_solar_multiannual($id,$month,$date,$years,$sy)
262{
263 global $specialdates;
264 $cy = sd_date('Y');
265 $cy -= $sy;
266 if ($cy==0 || $cy%$years==0)
267 {
268 $did = sd_getMonthStart($month-1);
269 $did += ($date-1);
270 $specialdates[$did] = $id;
271 }
272}
273
274function sd_solar_once($id,$month,$date,$year)
275{
276 if ($year == sd_date('Y'))
277 {
278 sd_solar_annual($id,$month,$date);
279 }
280}
281
282function sd_getDay($id)
283{
284 global $specialdates;
285 return $specialdates[$id];
286}
287
288function sd_lunar_monthly($id,$dotw,$wn)
289{
290 $i=1;
291 for ($i=1;$i<13;$i++) {
292 sd_lunar_annual($id,$i,$dotw,$wn);
293 }
294}
295
296function sd_date($format,$timestamp = 0)
297{
298 if ($timestamp == 0) {$timestamp = time();}
299 return date($format,$timestamp);
300}
301
302function sd_clearDate()
303{
304 $ys = strtotime('January 1 ' . date('Y'));
305 return $ys;
306}
307
308function sd_addDateIn($id,$dateid)
309{
310 global $specialdates;
311 $specialdates[$dateid] = $id;
312}
313
314?>
diff --git a/includes/template.php b/includes/template.php new file mode 100644 index 0000000..d777475 --- /dev/null +++ b/includes/template.php
@@ -0,0 +1,164 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/template.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25class FITemplate
26{
27
28 var $file;
29 var $tags;
30 var $blocks;
31 var $refs;
32
33 function FITemplate($filename)
34 {
35 $tfn = 'theme/' . $filename . '.tpl';
36 if (file_exists($tfn))
37 {
38 $this->file = $tfn;
39 } else {
40 throw new Exception($tfn . ' does not exist');
41 }
42 }
43
44 function add($name, $value)
45 {
46 $this->tags[$name] = $value;
47 }
48
49 function adds($tagarr)
50 {
51 foreach ($tagarr as $name => $value)
52 {
53 $this->add($name,$value);
54 }
55 }
56
57 function adds_block($block, $tagarr)
58 {
59 if (!isset($this->blocks[$block]))
60 {
61 $this->blocks[$block] = array('count' => 1);
62 }
63 foreach ($tagarr as $name => $value)
64 {
65 $this->blocks[$block][$this->blocks[$block]['count']][$name] = $value;
66 }
67 $this->blocks[$block]['count']++;
68 }
69
70 function add_ref($id, $block, $tagarr)
71 {
72 $this->adds_block($block,$tagarr);
73 $this->refs[$id] = &$this->blocks[$block][$this->blocks[$block]['count']-1];//'$this->blocks[\'' . $block . '\'][' . ($this->blocks[$block]['count']-1) . ']';
74 }
75
76 function adds_ref($id, $tagarr)
77 {
78 foreach ($tagarr as $name => $value)
79 {
80 $this->refs[$id][$name] = $value;
81 }
82 }
83 function adds_ref_sub($id, $block, $tagarr)
84 {
85 if (!isset($this->refs[$id][$block]))
86 {
87 $this->refs[$id][$block] = array('count' => 1);
88 }
89 foreach ($tagarr as $name => $value)
90 {
91 $this->refs[$id][$block][$this->refs[$id][$block]['count']][$name] = $value;
92 }
93 $this->refs[$id][$block]['count']++;
94 }
95
96 function display()
97 {
98 $template = file_get_contents($this->file);
99 while (preg_match('/<!--INCLUDE ([^>]*)-->/',$template) == 1)
100 {
101 preg_match('/<!--INCLUDE ([^>]*)-->/',$template,$mat);
102 $fname = $mat[1];
103 $itmp = new FITemplate($fname);
104 $template = str_replace('<!--INCLUDE ' . $fname . '-->',file_get_contents($itmp->file),$template);
105 }
106 if (isset($this->tags))
107 {
108 foreach ($this->tags as $name => $value)
109 {
110 $template = str_replace('<!--' . $name . '-->',$value,$template);
111 }
112 }
113 if (isset($this->blocks))
114 {
115 foreach ($this->blocks as $bname => $block)
116 {
117 $this->parseBlock($template, $bname, $block);
118 }
119 }
120 while (preg_match('/<!--BEGIN ([^>]*)-->/',$template) == 1)
121 {
122 preg_match('/<!--BEGIN ([^>]*)-->/',$template,$mat);
123 $bname = $mat[1];
124 $start = strpos($template,'<!--BEGIN ' . $bname . '-->');
125 $end = strpos($template,'<!--END ' . $bname . '-->');
126 $template = str_replace(substr($template,$start,$end-$start+strlen($bname)+11),'',$template);
127 }
128 $template = preg_replace('/<!--([^>]*)-->/','',$template);
129
130 echo($template);
131 }
132
133 function parseBlock(&$template, $bname, $block)
134 {
135 while (strpos($template,'<!--BEGIN ' . $bname . '-->') !== FALSE)
136 {
137 $start = strpos($template,'<!--BEGIN ' . $bname . '-->');
138 $end = strpos($template,'<!--END ' . $bname . '-->');
139 $gencont = substr($template,$start+strlen($bname)+13,$end-$start-strlen($bname)-13);
140 $blockcont = '';
141 foreach ($block as $lname => $blocktags)
142 {
143 if ($lname != 'count')
144 {
145 $scrcont = $gencont;
146 foreach ($blocktags as $name => $value)
147 {
148 if (!is_array($value))
149 {
150 $scrcont = str_replace('<!--' . $bname . '.' . $name . '-->',$value,$scrcont);
151 } else {
152 $this->parseBlock($scrcont, $bname . '.' . $name, $value);
153 }
154 }
155 $blockcont .= $scrcont;
156 }
157 }
158 $template = str_replace(substr($template,$start,$end-$start+strlen($bname)+11),$blockcont,$template);
159 }
160 }
161
162}
163
164?>
diff --git a/includes/updatePending.php b/includes/updatePending.php new file mode 100644 index 0000000..a96a2b6 --- /dev/null +++ b/includes/updatePending.php
@@ -0,0 +1,51 @@
1<?php
2/*
3 444444444
4 4::::::::4
5 4:::::::::4
6 4::::44::::4
7 4::::4 4::::4 Four Island
8 4::::4 4::::4
9 4::::4 4::::4 Written and maintained by Starla Insigna
104::::444444::::444
114::::::::::::::::4 includes/updatePending.php
124444444444:::::444
13 4::::4 Please do not use, reproduce or steal the
14 4::::4 contents of this file without explicit
15 4::::4 permission from Hatkirby.
16 44::::::44
17 4::::::::4
18 4444444444
19*/
20
21if (!defined('S_INCLUDE_FILE')) {define('S_INCLUDE_FILE',1);}
22
23require('headerproc.php');
24
25//$disablePendingQueue = 1; // Use this when Four Island goes down
26
27if (!isset($disablePendingQueue))
28{
29 $gettime = "SELECT * FROM config WHERE name = \"lastUpdate\"";
30 $gettime2 = mysql_query($gettime);
31 $gettime3 = mysql_fetch_array($gettime2);
32 if (($gettime3['value'] != date('md')) && (time() > strtotime('12:30')))
33 {
34 $cntpending = "SELECT COUNT(*) FROM pending";
35 $cntpending2 = mysql_query($cntpending);
36 $cntpending3 = mysql_fetch_array($cntpending2);
37 if ($cntpending3[0] != 0)
38 {
39 $getpost = "SELECT * FROM pending ORDER BY id ASC LIMIT 0,1";
40 $getpost2 = mysql_query($getpost);
41 $getpost3 = mysql_fetch_array($getpost2);
42
43 postBlogPost($getpost3['title'], $getpost3['author'], $getpost3['tag1'], $getpost3['tag2'], $getpost3['tag3'], $getpost3['text']);
44
45 $delpost = "DELETE FROM pending WHERE id = " . $getpost3['id'];
46 $delpost2 = mysql_query($delpost);
47 }
48 }
49}
50
51?>
diff --git a/includes/xml_parser.php b/includes/xml_parser.php new file mode 100644 index 0000000..15e02e0 --- /dev/null +++ b/includes/xml_parser.php
@@ -0,0 +1,427 @@
1<?php
2/*
3 * xml_parser.php
4 *
5 * @(#) $Header: /home/mlemos/cvsroot/xmlparser/xml_parser.php,v 1.19 2006/11/22 01:25:05 mlemos Exp $
6 *
7 */
8
9/*
10 * Parser error numbers:
11 *
12 * 1 - Could not create the XML parser
13 * 2 - Could not parse data
14 * 3 - Could not read from input stream
15 *
16 */
17
18$xml_parser_handlers=array();
19
20Function xml_parser_start_element_handler($parser,$name,$attrs)
21{
22 global $xml_parser_handlers;
23
24 if(!strcmp($xml_parser_handlers[$parser]->error,""))
25 $xml_parser_handlers[$parser]->StartElement($xml_parser_handlers[$parser],$name,$attrs);
26}
27
28Function xml_parser_end_element_handler($parser,$name)
29{
30 global $xml_parser_handlers;
31
32 if(!strcmp($xml_parser_handlers[$parser]->error,""))
33 $xml_parser_handlers[$parser]->EndElement($xml_parser_handlers[$parser],$name);
34}
35
36Function xml_parser_character_data_handler($parser,$data)
37{
38 global $xml_parser_handlers;
39
40 if(!strcmp($xml_parser_handlers[$parser]->error,""))
41 $xml_parser_handlers[$parser]->CharacterData($xml_parser_handlers[$parser],$data);
42}
43
44class xml_parser_handler_class
45{
46 var $xml_parser;
47 var $error_number=0;
48 var $error="";
49 var $error_code=0;
50 var $error_line,$error_column,$error_byte_index;
51 var $structure=array();
52 var $positions=array();
53 var $path="";
54 var $store_positions=0;
55 var $simplified_xml=0;
56 var $fail_on_non_simplified_xml=0;
57
58 Function SetError(&$object,$error_number,$error)
59 {
60 $object->error_number=$error_number;
61 $object->error=$error;
62 $object->error_line=xml_get_current_line_number($object->xml_parser);
63 $object->error_column=xml_get_current_column_number($object->xml_parser);
64 $object->error_byte_index=xml_get_current_byte_index($object->xml_parser);
65 }
66
67 Function SetElementData(&$object,$path,&$data)
68 {
69 $object->structure[$path]=$data;
70 if($object->store_positions)
71 {
72 $object->positions[$path]=array(
73 "Line"=>xml_get_current_line_number($object->xml_parser),
74 "Column"=>xml_get_current_column_number($object->xml_parser),
75 "Byte"=>xml_get_current_byte_index($object->xml_parser)
76 );
77 }
78 }
79
80 Function StartElement(&$object,$name,&$attrs)
81 {
82 if(strcmp($this->path,""))
83 {
84 $element=$object->structure[$this->path]["Elements"];
85 $object->structure[$this->path]["Elements"]++;
86 $this->path.=",$element";
87 }
88 else
89 {
90 $element=0;
91 $this->path="0";
92 }
93 $data=array(
94 "Tag"=>$name,
95 "Elements"=>0
96 );
97 if($object->simplified_xml)
98 {
99 if($object->fail_on_non_simplified_xml
100 && count($attrs)>0)
101 {
102 $this->SetError($object,2,"Simplified XML can not have attributes in tags");
103 return;
104 }
105 }
106 else
107 $data["Attributes"]=$attrs;
108 $this->SetElementData($object,$this->path,$data);
109 }
110
111 Function EndElement(&$object,$name)
112 {
113 $this->path=(($position=strrpos($this->path,",")) ? substr($this->path,0,$position) : "");
114 }
115
116 Function CharacterData(&$object,$data)
117 {
118 $element=$object->structure[$this->path]["Elements"];
119 $previous=$this->path.",".strval($element-1);
120 if($element>0
121 && GetType($object->structure[$previous])=="string")
122 $object->structure[$previous].=$data;
123 else
124 {
125 $this->SetElementData($object,$this->path.",$element",$data);
126 $object->structure[$this->path]["Elements"]++;
127 }
128 }
129};
130
131class xml_parser_class
132{
133 var $xml_parser=0;
134 var $parser_handler;
135 var $error="";
136 var $error_number=0;
137 var $error_line=0;
138 var $error_column=0;
139 var $error_byte_index=0;
140 var $error_code=0;
141 var $stream_buffer_size=4096;
142 var $structure=array();
143 var $positions=array();
144 var $store_positions=0;
145 var $case_folding=0;
146 var $target_encoding="ISO-8859-1";
147 var $simplified_xml=0;
148 var $fail_on_non_simplified_xml=0;
149
150 Function xml_parser_start_element_handler($parser,$name,$attrs)
151 {
152 if(!strcmp($this->error,""))
153 $this->parser_handler->StartElement($this,$name,$attrs);
154 }
155
156 Function xml_parser_end_element_handler($parser,$name)
157 {
158 if(!strcmp($this->error,""))
159 $this->parser_handler->EndElement($this,$name);
160 }
161
162 Function xml_parser_character_data_handler($parser,$data)
163 {
164 if(!strcmp($this->error,""))
165 $this->parser_handler->CharacterData($this,$data);
166 }
167
168 Function SetErrorPosition($error_number,$error,$line,$column,$byte_index)
169 {
170 $this->error_number=$error_number;
171 $this->error=$error;
172 $this->error_line=$line;
173 $this->error_column=$column;
174 $this->error_byte_index=$byte_index;
175 }
176
177 Function SetError($error_number,$error)
178 {
179 $this->error_number=$error_number;
180 $this->error=$error;
181 if($this->xml_parser)
182 {
183 $line=xml_get_current_line_number($this->xml_parser);
184 $column=xml_get_current_column_number($this->xml_parser);
185 $byte_index=xml_get_current_byte_index($this->xml_parser);
186 }
187 else
188 {
189 $line=$column=1;
190 $byte_index=0;
191 }
192 $this->SetErrorPosition($error_number,$error,$line,$column,$byte_index);
193 }
194
195 Function Parse($data,$end_of_data)
196 {
197 global $xml_parser_handlers;
198
199 if(strcmp($this->error,""))
200 return($this->error);
201 if(!$this->xml_parser)
202 {
203 if(!function_exists("xml_parser_create"))
204 {
205 $this->SetError(1,"XML support is not available in this PHP configuration");
206 return($this->error);
207 }
208 if(!($this->xml_parser=xml_parser_create()))
209 {
210 $this->SetError(1,"Could not create the XML parser");
211 return($this->error);
212 }
213 xml_parser_set_option($this->xml_parser,XML_OPTION_CASE_FOLDING,$this->case_folding);
214 xml_parser_set_option($this->xml_parser,XML_OPTION_TARGET_ENCODING,$this->target_encoding);
215 if(function_exists("xml_set_object"))
216 {
217 xml_set_object($this->xml_parser,$this);
218 $this->parser_handler=new xml_parser_handler_class;
219 $this->structure=array();
220 $this->positions=array();
221 }
222 else
223 {
224 $xml_parser_handlers[$this->xml_parser]=new xml_parser_handler_class;
225 $xml_parser_handlers[$this->xml_parser]->xml_parser=$this->xml_parser;
226 $xml_parser_handlers[$this->xml_parser]->store_positions=$this->store_positions;
227 $xml_parser_handlers[$this->xml_parser]->simplified_xml=$this->simplified_xml;
228 $xml_parser_handlers[$this->xml_parser]->fail_on_non_simplified_xml=$this->fail_on_non_simplified_xml;
229 }
230 xml_set_element_handler($this->xml_parser,"xml_parser_start_element_handler","xml_parser_end_element_handler");
231 xml_set_character_data_handler($this->xml_parser,"xml_parser_character_data_handler");
232 }
233 $parser_ok=xml_parse($this->xml_parser,$data,$end_of_data);
234 if(!function_exists("xml_set_object"))
235 $this->error=$xml_parser_handlers[$this->xml_parser]->error;
236 if(!strcmp($this->error,""))
237 {
238 if($parser_ok)
239 {
240 if($end_of_data)
241 {
242 if(function_exists("xml_set_object"))
243 Unset($this->parser_handler);
244 else
245 {
246 $this->structure=$xml_parser_handlers[$this->xml_parser]->structure;
247 $this->positions=$xml_parser_handlers[$this->xml_parser]->positions;
248 Unset($xml_parser_handlers[$this->xml_parser]);
249 }
250 xml_parser_free($this->xml_parser);
251 $this->xml_parser=0;
252 }
253 }
254 else
255 $this->SetError(2,"Could not parse data: ".xml_error_string($this->error_code=xml_get_error_code($this->xml_parser)));
256 }
257 else
258 {
259 if(!function_exists("xml_set_object"))
260 {
261 $this->error_number=$xml_parser_handlers[$this->xml_parser]->error_number;
262 $this->error_code=$xml_parser_handlers[$this->xml_parser]->error_code;
263 $this->error_line=$xml_parser_handlers[$this->xml_parser]->error_line;
264 $this->error_column=$xml_parser_handlers[$this->xml_parser]->error_column;
265 $this->error_byte_index=$xml_parser_handlers[$this->xml_parser]->error_byte_index;
266 }
267 }
268 return($this->error);
269 }
270
271 Function VerifyWhiteSpace($path)
272 {
273 if($this->store_positions)
274 {
275 $line=$parser->positions[$path]["Line"];
276 $column=$parser->positions[$path]["Column"];
277 $byte_index=$parser->positions[$path]["Byte"];
278 }
279 else
280 {
281 $line=$column=1;
282 $byte_index=0;
283 }
284 if(!IsSet($this->structure[$path]))
285 {
286 $this->SetErrorPosition(2,"element path does not exist",$line,$column,$byte_index);
287 return($this->error);
288 }
289 if(GetType($this->structure[$path])!="string")
290 {
291 $this->SetErrorPosition(2,"element is not data",$line,$column,$byte_index);
292 return($this->error);
293 }
294 $data=$this->structure[$path];
295 for($previous_return=0,$position=0;$position<strlen($data);$position++)
296 {
297 switch($data[$position])
298 {
299 case " ":
300 case "\t":
301 $column++;
302 $byte_index++;
303 $previous_return=0;
304 break;
305 case "\n":
306 if(!$previous_return)
307 $line++;
308 $column=1;
309 $byte_index++;
310 $previous_return=0;
311 break;
312 case "\r":
313 $line++;
314 $column=1;
315 $byte_index++;
316 $previous_return=1;
317 break;
318 default:
319 $this->SetErrorPosition(2,"data is not white space",$line,$column,$byte_index);
320 return($this->error);
321 }
322 }
323 return("");
324 }
325
326 Function ParseStream($stream)
327 {
328 if(strcmp($this->error,""))
329 return($this->error);
330 do
331 {
332 if(!($data=@fread($stream,$this->stream_buffer_size)))
333 {
334 if(!feof($stream))
335 {
336 $this->SetError(3,"Could not read from input stream".(IsSet($php_errormsg) ? ': '.$php_errormsg : ''));
337 break;
338 }
339 }
340 if(strcmp($error=$this->Parse($data,feof($stream)),""))
341 break;
342 }
343 while(!feof($stream));
344 return($this->error);
345 }
346
347 Function ParseFile($file)
348 {
349 if(!file_exists($file))
350 return("the XML file to parse ($file) does not exist");
351 if(!($definition=@fopen($file,"r")))
352 return("could not open the XML file ($file)".(IsSet($php_errormsg) ? ': '.$php_errormsg : ''));
353 $error=$this->ParseStream($definition);
354 fclose($definition);
355 return($error);
356 }
357};
358
359Function XMLParseFile(&$parser,$file,$store_positions,$cache="",$case_folding=0,$target_encoding="ISO-8859-1",$simplified_xml=0,$fail_on_non_simplified_xml=0)
360{
361 if(!file_exists($file))
362 return("the XML file to parse ($file) does not exist");
363 if(strcmp($cache,""))
364 {
365 if(file_exists($cache)
366 && filemtime($file)<=filemtime($cache))
367 {
368 if(($cache_file=@fopen($cache,"r")))
369 {
370 if(function_exists("set_file_buffer"))
371 set_file_buffer($cache_file,0);
372 if(!($cache_contents=@fread($cache_file,filesize($cache))))
373 $error="could not read from the XML cache file $cache".(IsSet($php_errormsg) ? ': '.$php_errormsg : '');
374 else
375 $error="";
376 fclose($cache_file);
377 if(!strcmp($error,""))
378 {
379 if(GetType($parser=unserialize($cache_contents))=="object"
380 && IsSet($parser->structure))
381 {
382 if(!IsSet($parser->simplified_xml))
383 $parser->simplified_xml=0;
384 if(($simplified_xml
385 || !$parser->simplified_xml)
386 && (!$store_positions
387 || $parser->store_positions))
388 {
389 return("");
390 }
391 }
392 else
393 $error="it was not specified a valid cache object in XML file ($cache)";
394 }
395 }
396 else
397 $error="could not open cache XML file ($cache)".(IsSet($php_errormsg) ? ': '.$php_errormsg : '');
398 if(strcmp($error,""))
399 return($error);
400 }
401 }
402 $parser=new xml_parser_class;
403 $parser->store_positions=$store_positions;
404 $parser->case_folding=$case_folding;
405 $parser->target_encoding=$target_encoding;
406 $parser->simplified_xml=$simplified_xml;
407 $parser->fail_on_non_simplified_xml=$fail_on_non_simplified_xml;
408 if(!strcmp($error=$parser->ParseFile($file),"")
409 && strcmp($cache,""))
410 {
411 if(($cache_file=@fopen($cache,"w")))
412 {
413 if(function_exists("set_file_buffer"))
414 set_file_buffer($cache_file,0);
415 if(!@fwrite($cache_file,serialize($parser))
416 || !@fclose($cache_file))
417 $error="could to write to the XML cache file ($cache)".(IsSet($php_errormsg) ? ': '.$php_errormsg : '');
418 if(strcmp($error,""))
419 unlink($cache);
420 }
421 else
422 $error="could not open for writing to the cache file ($cache)".(IsSet($php_errormsg) ? ': '.$php_errormsg : '');
423 }
424 return($error);
425}
426
427?> \ No newline at end of file
diff --git a/includes/xmlrpc/array_key_exists.php b/includes/xmlrpc/array_key_exists.php new file mode 100644 index 0000000..c5ae519 --- /dev/null +++ b/includes/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/includes/xmlrpc/is_a.php b/includes/xmlrpc/is_a.php new file mode 100644 index 0000000..d98db1f --- /dev/null +++ b/includes/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/includes/xmlrpc/is_callable.php b/includes/xmlrpc/is_callable.php new file mode 100644 index 0000000..b769c41 --- /dev/null +++ b/includes/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/includes/xmlrpc/is_scalar.php b/includes/xmlrpc/is_scalar.php new file mode 100644 index 0000000..c8f2bfc --- /dev/null +++ b/includes/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/includes/xmlrpc/var_export.php b/includes/xmlrpc/var_export.php new file mode 100644 index 0000000..3a5ac3f --- /dev/null +++ b/includes/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/includes/xmlrpc/version_compare.php b/includes/xmlrpc/version_compare.php new file mode 100644 index 0000000..fc3abac --- /dev/null +++ b/includes/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/includes/xmlrpc/xmlrpc.inc b/includes/xmlrpc/xmlrpc.inc new file mode 100644 index 0000000..06c6e44 --- /dev/null +++ b/includes/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/includes/xmlrpc/xmlrpc_wrappers.inc b/includes/xmlrpc/xmlrpc_wrappers.inc new file mode 100644 index 0000000..cd0a56f --- /dev/null +++ b/includes/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/includes/xmlrpc/xmlrpcs.inc b/includes/xmlrpc/xmlrpcs.inc new file mode 100644 index 0000000..9588919 --- /dev/null +++ b/includes/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