Lib_PEAR - Version 1.3.1

Version Notes

1.3.1

Download this release

Release Info

Developer Magento Core Team
Extension Lib_PEAR
Version 1.3.1
Comparing to
See all releases


Version 1.3.1

lib/PEAR/HTTP/HTTP.php ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
+
5
+ /**
6
+ * HTTP
7
+ *
8
+ * PHP versions 4 and 5
9
+ *
10
+ * @category HTTP
11
+ * @package HTTP
12
+ * @author Stig Bakken <ssb@fast.no>
13
+ * @author Sterling Hughes <sterling@php.net>
14
+ * @author Tomas V.V.Cox <cox@idecnet.com>
15
+ * @author Richard Heyes <richard@php.net>
16
+ * @author Philippe Jausions <jausions@php.net>
17
+ * @author Michael Wallner <mike@php.net>
18
+ * @copyright 2002-2008 The Authors
19
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
20
+ * @version CVS: $Id: HTTP.php,v 1.56 2008/08/31 20:15:43 jausions Exp $
21
+ * @link http://pear.php.net/package/HTTP
22
+ */
23
+
24
+ /**
25
+ * Miscellaneous HTTP Utilities
26
+ *
27
+ * PEAR::HTTP provides static shorthand methods for generating HTTP dates,
28
+ * issueing HTTP HEAD requests, building absolute URIs, firing redirects and
29
+ * negotiating user preferred language.
30
+ *
31
+ * @category HTTP
32
+ * @package HTTP
33
+ * @author Stig Bakken <ssb@fast.no>
34
+ * @author Sterling Hughes <sterling@php.net>
35
+ * @author Tomas V.V.Cox <cox@idecnet.com>
36
+ * @author Richard Heyes <richard@php.net>
37
+ * @author Philippe Jausions <jausions@php.net>
38
+ * @author Michael Wallner <mike@php.net>
39
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
40
+ * @abstract
41
+ * @version Release: $Revision: 1.56 $
42
+ * @link http://pear.php.net/package/HTTP
43
+ */
44
+ class HTTP
45
+ {
46
+ /**
47
+ * Formats a RFC compliant GMT date HTTP header. This function honors the
48
+ * "y2k_compliance" php.ini directive and formats the GMT date corresponding
49
+ * to either RFC850 or RFC822.
50
+ *
51
+ * @param mixed $time unix timestamp or date (default = current time)
52
+ *
53
+ * @return mixed GMT date string, or false for an invalid $time parameter
54
+ * @access public
55
+ * @static
56
+ */
57
+ function Date($time = null)
58
+ {
59
+ if (!isset($time)) {
60
+ $time = time();
61
+ } elseif (!is_numeric($time) && (-1 === $time = strtotime($time))) {
62
+ return false;
63
+ }
64
+
65
+ // RFC822 or RFC850
66
+ $format = ini_get('y2k_compliance') ? 'D, d M Y' : 'l, d-M-y';
67
+
68
+ return gmdate($format .' H:i:s \G\M\T', $time);
69
+ }
70
+
71
+ /**
72
+ * Negotiates language with the user's browser through the Accept-Language
73
+ * HTTP header or the user's host address. Language codes are generally in
74
+ * the form "ll" for a language spoken in only one country, or "ll-CC" for a
75
+ * language spoken in a particular country. For example, U.S. English is
76
+ * "en-US", while British English is "en-UK". Portugese as spoken in
77
+ * Portugal is "pt-PT", while Brazilian Portugese is "pt-BR".
78
+ *
79
+ * Quality factors in the Accept-Language: header are supported, e.g.:
80
+ * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
81
+ *
82
+ * <code>
83
+ * require_once 'HTTP.php';
84
+ * $langs = array(
85
+ * 'en' => 'locales/en',
86
+ * 'en-US' => 'locales/en',
87
+ * 'en-UK' => 'locales/en',
88
+ * 'de' => 'locales/de',
89
+ * 'de-DE' => 'locales/de',
90
+ * 'de-AT' => 'locales/de',
91
+ * );
92
+ * $neg = HTTP::negotiateLanguage($langs);
93
+ * $dir = $langs[$neg];
94
+ * </code>
95
+ *
96
+ * @param array $supported An associative array of supported languages,
97
+ * whose values must evaluate to true.
98
+ * @param string $default The default language to use if none is found.
99
+ *
100
+ * @return string The negotiated language result or the supplied default.
101
+ * @static
102
+ * @access public
103
+ */
104
+ function negotiateLanguage($supported, $default = 'en-US')
105
+ {
106
+ $supp = array();
107
+ foreach ($supported as $lang => $isSupported) {
108
+ if ($isSupported) {
109
+ $supp[strtolower($lang)] = $lang;
110
+ }
111
+ }
112
+
113
+ if (!count($supp)) {
114
+ return $default;
115
+ }
116
+
117
+ if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
118
+ $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_LANGUAGE'],
119
+ $supp);
120
+ if (!is_null($match)) {
121
+ return $match;
122
+ }
123
+ }
124
+
125
+ if (isset($_SERVER['REMOTE_HOST'])) {
126
+ $lang = strtolower(end($h = explode('.', $_SERVER['REMOTE_HOST'])));
127
+ if (isset($supp[$lang])) {
128
+ return $supp[$lang];
129
+ }
130
+ }
131
+
132
+ return $default;
133
+ }
134
+
135
+ /**
136
+ * Negotiates charset with the user's browser through the Accept-Charset
137
+ * HTTP header.
138
+ *
139
+ * Quality factors in the Accept-Charset: header are supported, e.g.:
140
+ * Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
141
+ *
142
+ * <code>
143
+ * require_once 'HTTP.php';
144
+ * $charsets = array(
145
+ * 'UTF-8',
146
+ * 'ISO-8859-1',
147
+ * );
148
+ * $charset = HTTP::negotiateCharset($charsets);
149
+ * </code>
150
+ *
151
+ * @param array $supported An array of supported charsets
152
+ * @param string $default The default charset to use if none is found.
153
+ *
154
+ * @return string The negotiated language result or the supplied default.
155
+ * @static
156
+ * @author Philippe Jausions <jausions@php.net>
157
+ * @access public
158
+ * @since 1.4.1
159
+ */
160
+ function negotiateCharset($supported, $default = 'ISO-8859-1')
161
+ {
162
+ $supp = array();
163
+ foreach ($supported as $charset) {
164
+ $supp[strtolower($charset)] = $charset;
165
+ }
166
+
167
+ if (!count($supp)) {
168
+ return $default;
169
+ }
170
+
171
+ if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
172
+ $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_CHARSET'],
173
+ $supp);
174
+ if (!is_null($match)) {
175
+ return $match;
176
+ }
177
+ }
178
+
179
+ return $default;
180
+ }
181
+
182
+ /**
183
+ * Negotiates content type with the user's browser through the Accept
184
+ * HTTP header.
185
+ *
186
+ * Quality factors in the Accept: header are supported, e.g.:
187
+ * Accept: application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8
188
+ *
189
+ * <code>
190
+ * require_once 'HTTP.php';
191
+ * $contentType = array(
192
+ * 'application/xhtml+xml',
193
+ * 'application/xml',
194
+ * 'text/html',
195
+ * 'text/plain',
196
+ * );
197
+ * $mime = HTTP::negotiateContentType($contentType);
198
+ * </code>
199
+ *
200
+ * @param array $supported An associative array of supported MIME types.
201
+ * @param string $default The default type to use if none match.
202
+ *
203
+ * @return string The negotiated MIME type result or the supplied default.
204
+ * @static
205
+ * @author Philippe Jausions <jausions@php.net>
206
+ * @access public
207
+ * @since 1.4.1
208
+ */
209
+ function negotiateMimeType($supported, $default)
210
+ {
211
+ $supp = array();
212
+ foreach ($supported as $type) {
213
+ $supp[strtolower($type)] = $type;
214
+ }
215
+
216
+ if (!count($supp)) {
217
+ return $default;
218
+ }
219
+
220
+ if (isset($_SERVER['HTTP_ACCEPT'])) {
221
+ $accepts = HTTP::_sortAccept($_SERVER['HTTP_ACCEPT']);
222
+
223
+ foreach ($accepts as $type => $q) {
224
+ if (substr($type, -2) != '/*') {
225
+ if (isset($supp[$type])) {
226
+ return $supp[$type];
227
+ }
228
+ continue;
229
+ }
230
+ if ($type == '*/*') {
231
+ return array_shift($supp);
232
+ }
233
+ list($general, $specific) = explode('/', $type);
234
+ $general .= '/';
235
+ $len = strlen($general);
236
+ foreach ($supp as $mime => $t) {
237
+ if (strncasecmp($general, $mime, $len) == 0) {
238
+ return $t;
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ return $default;
245
+ }
246
+
247
+ /**
248
+ * Parses a weighed "Accept" HTTP header and matches it against a list
249
+ * of supported options
250
+ *
251
+ * @param string $header The HTTP "Accept" header to parse
252
+ * @param array $supported A list of supported values
253
+ *
254
+ * @return string|NULL a matched option, or NULL if no match
255
+ * @access private
256
+ * @static
257
+ */
258
+ function _matchAccept($header, $supported)
259
+ {
260
+ $matches = HTTP::_sortAccept($header);
261
+ foreach ($matches as $key => $q) {
262
+ if (isset($supported[$key])) {
263
+ return $supported[$key];
264
+ }
265
+ }
266
+ // If any (i.e. "*") is acceptable, return the first supported format
267
+ if (isset($matches['*'])) {
268
+ return array_shift($supported);
269
+ }
270
+ return null;
271
+ }
272
+
273
+ /**
274
+ * Parses and sorts a weighed "Accept" HTTP header
275
+ *
276
+ * @param string $header The HTTP "Accept" header to parse
277
+ *
278
+ * @return array a sorted list of "accept" options
279
+ * @access private
280
+ * @static
281
+ */
282
+ function _sortAccept($header)
283
+ {
284
+ $matches = array();
285
+ foreach (explode(',', $header) as $option) {
286
+ $option = array_map('trim', explode(';', $option));
287
+
288
+ $l = strtolower($option[0]);
289
+ if (isset($option[1])) {
290
+ $q = (float) str_replace('q=', '', $option[1]);
291
+ } else {
292
+ $q = null;
293
+ // Assign default low weight for generic values
294
+ if ($l == '*/*') {
295
+ $q = 0.01;
296
+ } elseif (substr($l, -1) == '*') {
297
+ $q = 0.02;
298
+ }
299
+ }
300
+ // Unweighted values, get high weight by their position in the
301
+ // list
302
+ $matches[$l] = isset($q) ? $q : 1000 - count($matches);
303
+ }
304
+ arsort($matches, SORT_NUMERIC);
305
+ return $matches;
306
+ }
307
+
308
+ /**
309
+ * Sends a "HEAD" HTTP command to a server and returns the headers
310
+ * as an associative array.
311
+ *
312
+ * Example output could be:
313
+ * <code>
314
+ * Array
315
+ * (
316
+ * [response_code] => 200 // The HTTP response code
317
+ * [response] => HTTP/1.1 200 OK // The full HTTP response string
318
+ * [Date] => Fri, 11 Jan 2002 01:41:44 GMT
319
+ * [Server] => Apache/1.3.20 (Unix) PHP/4.1.1
320
+ * [X-Powered-By] => PHP/4.1.1
321
+ * [Connection] => close
322
+ * [Content-Type] => text/html
323
+ * )
324
+ * </code>
325
+ *
326
+ * @param string $url A valid URL, e.g.: http://pear.php.net/credits.php
327
+ * @param integer $timeout Timeout in seconds (default = 10)
328
+ *
329
+ * @return array Returns associative array of response headers on success
330
+ * or PEAR error on failure.
331
+ * @static
332
+ * @access public
333
+ * @see HTTP_Client::head()
334
+ * @see HTTP_Request
335
+ */
336
+ function head($url, $timeout = 10)
337
+ {
338
+ $p = parse_url($url);
339
+ if (!isset($p['scheme'])) {
340
+ $p = parse_url(HTTP::absoluteURI($url));
341
+ } elseif ($p['scheme'] != 'http') {
342
+ return HTTP::raiseError('Unsupported protocol: '. $p['scheme']);
343
+ }
344
+
345
+ $port = isset($p['port']) ? $p['port'] : 80;
346
+
347
+ if (!$fp = @fsockopen($p['host'], $port, $eno, $estr, $timeout)) {
348
+ return HTTP::raiseError("Connection error: $estr ($eno)");
349
+ }
350
+
351
+ $path = !empty($p['path']) ? $p['path'] : '/';
352
+ $path .= !empty($p['query']) ? '?' . $p['query'] : '';
353
+
354
+ fputs($fp, "HEAD $path HTTP/1.0\r\n");
355
+ fputs($fp, 'Host: ' . $p['host'] . ':' . $port . "\r\n");
356
+ fputs($fp, "Connection: close\r\n\r\n");
357
+
358
+ $response = rtrim(fgets($fp, 4096));
359
+ if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $response, $status)) {
360
+ $headers['response_code'] = $status[1];
361
+ }
362
+ $headers['response'] = $response;
363
+
364
+ while ($line = fgets($fp, 4096)) {
365
+ if (!trim($line)) {
366
+ break;
367
+ }
368
+ if (($pos = strpos($line, ':')) !== false) {
369
+ $header = substr($line, 0, $pos);
370
+ $value = trim(substr($line, $pos + 1));
371
+
372
+ $headers[$header] = $value;
373
+ }
374
+ }
375
+ fclose($fp);
376
+ return $headers;
377
+ }
378
+
379
+ /**
380
+ * This function redirects the client. This is done by issuing
381
+ * a "Location" header and exiting if wanted. If you set $rfc2616 to true
382
+ * HTTP will output a hypertext note with the location of the redirect.
383
+ *
384
+ * @param string $url URL where the redirect should go to.
385
+ * @param bool $exit Whether to exit immediately after redirection.
386
+ * @param bool $rfc2616 Wheter to output a hypertext note where we're
387
+ * redirecting to (Redirecting to
388
+ * <a href="...">...</a>.)
389
+ *
390
+ * @return boolean Returns TRUE on succes (or exits) or FALSE if headers
391
+ * have already been sent.
392
+ * @static
393
+ * @access public
394
+ */
395
+ function redirect($url, $exit = true, $rfc2616 = false)
396
+ {
397
+ if (headers_sent()) {
398
+ return false;
399
+ }
400
+
401
+ $url = HTTP::absoluteURI($url);
402
+ header('Location: '. $url);
403
+
404
+ if ($rfc2616 && isset($_SERVER['REQUEST_METHOD'])
405
+ && $_SERVER['REQUEST_METHOD'] != 'HEAD') {
406
+ echo '
407
+ <p>Redirecting to: <a href="'.str_replace('"', '%22', $url).'">'
408
+ .htmlspecialchars($url).'</a>.</p>
409
+ <script type="text/javascript">
410
+ //<![CDATA[
411
+ if (location.replace == null) {
412
+ location.replace = location.assign;
413
+ }
414
+ location.replace("'.str_replace('"', '\\"', $url).'");
415
+ // ]]>
416
+ </script>';
417
+ }
418
+ if ($exit) {
419
+ exit;
420
+ }
421
+ return true;
422
+ }
423
+
424
+ /**
425
+ * This function returns the absolute URI for the partial URL passed.
426
+ * The current scheme (HTTP/HTTPS), host server, port, current script
427
+ * location are used if necessary to resolve any relative URLs.
428
+ *
429
+ * Offsets potentially created by PATH_INFO are taken care of to resolve
430
+ * relative URLs to the current script.
431
+ *
432
+ * You can choose a new protocol while resolving the URI. This is
433
+ * particularly useful when redirecting a web browser using relative URIs
434
+ * and to switch from HTTP to HTTPS, or vice-versa, at the same time.
435
+ *
436
+ * @param string $url Absolute or relative URI the redirect should
437
+ * go to.
438
+ * @param string $protocol Protocol to use when redirecting URIs.
439
+ * @param integer $port A new port number.
440
+ *
441
+ * @return string The absolute URI.
442
+ * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
443
+ * @static
444
+ * @access public
445
+ */
446
+ function absoluteURI($url = null, $protocol = null, $port = null)
447
+ {
448
+ // filter CR/LF
449
+ $url = str_replace(array("\r", "\n"), ' ', $url);
450
+
451
+ // Mess around protocol and port with already absolute URIs
452
+ if (preg_match('!^([a-z0-9]+)://!i', $url)) {
453
+ if (empty($protocol) && empty($port)) {
454
+ return $url;
455
+ }
456
+ if (!empty($protocol)) {
457
+ $url = $protocol .':'. end($array = explode(':', $url, 2));
458
+ }
459
+ if (!empty($port)) {
460
+ $url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i',
461
+ '\1:'. $port, $url);
462
+ }
463
+ return $url;
464
+ }
465
+
466
+ $host = 'localhost';
467
+ if (!empty($_SERVER['HTTP_HOST'])) {
468
+ list($host) = explode(':', $_SERVER['HTTP_HOST']);
469
+ } elseif (!empty($_SERVER['SERVER_NAME'])) {
470
+ list($host) = explode(':', $_SERVER['SERVER_NAME']);
471
+ }
472
+
473
+ if (empty($protocol)) {
474
+ if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) {
475
+ $protocol = 'https';
476
+ } else {
477
+ $protocol = 'http';
478
+ }
479
+ if (!isset($port) || $port != intval($port)) {
480
+ $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
481
+ }
482
+ }
483
+
484
+ if ($protocol == 'http' && $port == 80) {
485
+ unset($port);
486
+ }
487
+ if ($protocol == 'https' && $port == 443) {
488
+ unset($port);
489
+ }
490
+
491
+ $server = $protocol.'://'.$host.(isset($port) ? ':'.$port : '');
492
+
493
+ $uriAll = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI']
494
+ : $_SERVER['PHP_SELF'];
495
+ if (false !== ($q = strpos($uriAll, '?'))) {
496
+ $uriBase = substr($uriAll, 0, $q);
497
+ } else {
498
+ $uriBase = $uriAll;
499
+ }
500
+ if (!strlen($url) || $url{0} == '#') {
501
+ $url = $uriAll.$url;
502
+ } elseif ($url{0} == '?') {
503
+ $url = $uriBase.$url;
504
+ }
505
+ if ($url{0} == '/') {
506
+ return $server . $url;
507
+ }
508
+
509
+ // Adjust for PATH_INFO if needed
510
+ if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])) {
511
+ $path = dirname(substr($uriBase, 0,
512
+ -strlen($_SERVER['PATH_INFO'])));
513
+ } else {
514
+ /**
515
+ * Fixes bug #12672 PHP_SELF ending on / causes incorrect redirects
516
+ *
517
+ * @link http://pear.php.net/bugs/12672
518
+ */
519
+ $path = dirname($uriBase.'-');
520
+ }
521
+
522
+ if (substr($path = strtr($path, '\\', '/'), -1) != '/') {
523
+ $path .= '/';
524
+ }
525
+
526
+ return $server . $path . $url;
527
+ }
528
+
529
+ /**
530
+ * Raise Error
531
+ *
532
+ * Lazy raising of PEAR_Errors.
533
+ *
534
+ * @param mixed $error Error
535
+ * @param integer $code Error code
536
+ *
537
+ * @return object PEAR_Error
538
+ * @static
539
+ * @access protected
540
+ */
541
+ function raiseError($error = null, $code = null)
542
+ {
543
+ include_once 'PEAR.php';
544
+ return PEAR::raiseError($error, $code);
545
+ }
546
+ }
547
+
548
+ ?>
lib/PEAR/HTTP/Request.php ADDED
@@ -0,0 +1,1521 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class for performing HTTP requests
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE:
8
+ *
9
+ * Copyright (c) 2002-2007, Richard Heyes
10
+ * All rights reserved.
11
+ *
12
+ * Redistribution and use in source and binary forms, with or without
13
+ * modification, are permitted provided that the following conditions
14
+ * are met:
15
+ *
16
+ * o Redistributions of source code must retain the above copyright
17
+ * notice, this list of conditions and the following disclaimer.
18
+ * o Redistributions in binary form must reproduce the above copyright
19
+ * notice, this list of conditions and the following disclaimer in the
20
+ * documentation and/or other materials provided with the distribution.
21
+ * o The names of the authors may not be used to endorse or promote
22
+ * products derived from this software without specific prior written
23
+ * 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 FOR
28
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ *
37
+ * @category HTTP
38
+ * @package HTTP_Request
39
+ * @author Richard Heyes <richard@phpguru.org>
40
+ * @author Alexey Borzov <avb@php.net>
41
+ * @copyright 2002-2007 Richard Heyes
42
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
43
+ * @version CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $
44
+ * @link http://pear.php.net/package/HTTP_Request/
45
+ */
46
+
47
+ /**
48
+ * PEAR and PEAR_Error classes (for error handling)
49
+ */
50
+ require_once 'PEAR.php';
51
+ /**
52
+ * Socket class
53
+ */
54
+ require_once 'Net/Socket.php';
55
+ /**
56
+ * URL handling class
57
+ */
58
+ require_once 'Net/URL.php';
59
+
60
+ /**#@+
61
+ * Constants for HTTP request methods
62
+ */
63
+ define('HTTP_REQUEST_METHOD_GET', 'GET', true);
64
+ define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
65
+ define('HTTP_REQUEST_METHOD_POST', 'POST', true);
66
+ define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
67
+ define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
68
+ define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
69
+ define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
70
+ /**#@-*/
71
+
72
+ /**#@+
73
+ * Constants for HTTP request error codes
74
+ */
75
+ define('HTTP_REQUEST_ERROR_FILE', 1);
76
+ define('HTTP_REQUEST_ERROR_URL', 2);
77
+ define('HTTP_REQUEST_ERROR_PROXY', 4);
78
+ define('HTTP_REQUEST_ERROR_REDIRECTS', 8);
79
+ define('HTTP_REQUEST_ERROR_RESPONSE', 16);
80
+ define('HTTP_REQUEST_ERROR_GZIP_METHOD', 32);
81
+ define('HTTP_REQUEST_ERROR_GZIP_READ', 64);
82
+ define('HTTP_REQUEST_ERROR_GZIP_DATA', 128);
83
+ define('HTTP_REQUEST_ERROR_GZIP_CRC', 256);
84
+ /**#@-*/
85
+
86
+ /**#@+
87
+ * Constants for HTTP protocol versions
88
+ */
89
+ define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
90
+ define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
91
+ /**#@-*/
92
+
93
+ if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
94
+ /**
95
+ * Whether string functions are overloaded by their mbstring equivalents
96
+ */
97
+ define('HTTP_REQUEST_MBSTRING', true);
98
+ } else {
99
+ /**
100
+ * @ignore
101
+ */
102
+ define('HTTP_REQUEST_MBSTRING', false);
103
+ }
104
+
105
+ /**
106
+ * Class for performing HTTP requests
107
+ *
108
+ * Simple example (fetches yahoo.com and displays it):
109
+ * <code>
110
+ * $a = &new HTTP_Request('http://www.yahoo.com/');
111
+ * $a->sendRequest();
112
+ * echo $a->getResponseBody();
113
+ * </code>
114
+ *
115
+ * @category HTTP
116
+ * @package HTTP_Request
117
+ * @author Richard Heyes <richard@phpguru.org>
118
+ * @author Alexey Borzov <avb@php.net>
119
+ * @version Release: 1.4.4
120
+ */
121
+ class HTTP_Request
122
+ {
123
+ /**#@+
124
+ * @access private
125
+ */
126
+ /**
127
+ * Instance of Net_URL
128
+ * @var Net_URL
129
+ */
130
+ var $_url;
131
+
132
+ /**
133
+ * Type of request
134
+ * @var string
135
+ */
136
+ var $_method;
137
+
138
+ /**
139
+ * HTTP Version
140
+ * @var string
141
+ */
142
+ var $_http;
143
+
144
+ /**
145
+ * Request headers
146
+ * @var array
147
+ */
148
+ var $_requestHeaders;
149
+
150
+ /**
151
+ * Basic Auth Username
152
+ * @var string
153
+ */
154
+ var $_user;
155
+
156
+ /**
157
+ * Basic Auth Password
158
+ * @var string
159
+ */
160
+ var $_pass;
161
+
162
+ /**
163
+ * Socket object
164
+ * @var Net_Socket
165
+ */
166
+ var $_sock;
167
+
168
+ /**
169
+ * Proxy server
170
+ * @var string
171
+ */
172
+ var $_proxy_host;
173
+
174
+ /**
175
+ * Proxy port
176
+ * @var integer
177
+ */
178
+ var $_proxy_port;
179
+
180
+ /**
181
+ * Proxy username
182
+ * @var string
183
+ */
184
+ var $_proxy_user;
185
+
186
+ /**
187
+ * Proxy password
188
+ * @var string
189
+ */
190
+ var $_proxy_pass;
191
+
192
+ /**
193
+ * Post data
194
+ * @var array
195
+ */
196
+ var $_postData;
197
+
198
+ /**
199
+ * Request body
200
+ * @var string
201
+ */
202
+ var $_body;
203
+
204
+ /**
205
+ * A list of methods that MUST NOT have a request body, per RFC 2616
206
+ * @var array
207
+ */
208
+ var $_bodyDisallowed = array('TRACE');
209
+
210
+ /**
211
+ * Methods having defined semantics for request body
212
+ *
213
+ * Content-Length header (indicating that the body follows, section 4.3 of
214
+ * RFC 2616) will be sent for these methods even if no body was added
215
+ *
216
+ * @var array
217
+ */
218
+ var $_bodyRequired = array('POST', 'PUT');
219
+
220
+ /**
221
+ * Files to post
222
+ * @var array
223
+ */
224
+ var $_postFiles = array();
225
+
226
+ /**
227
+ * Connection timeout.
228
+ * @var float
229
+ */
230
+ var $_timeout;
231
+
232
+ /**
233
+ * HTTP_Response object
234
+ * @var HTTP_Response
235
+ */
236
+ var $_response;
237
+
238
+ /**
239
+ * Whether to allow redirects
240
+ * @var boolean
241
+ */
242
+ var $_allowRedirects;
243
+
244
+ /**
245
+ * Maximum redirects allowed
246
+ * @var integer
247
+ */
248
+ var $_maxRedirects;
249
+
250
+ /**
251
+ * Current number of redirects
252
+ * @var integer
253
+ */
254
+ var $_redirects;
255
+
256
+ /**
257
+ * Whether to append brackets [] to array variables
258
+ * @var bool
259
+ */
260
+ var $_useBrackets = true;
261
+
262
+ /**
263
+ * Attached listeners
264
+ * @var array
265
+ */
266
+ var $_listeners = array();
267
+
268
+ /**
269
+ * Whether to save response body in response object property
270
+ * @var bool
271
+ */
272
+ var $_saveBody = true;
273
+
274
+ /**
275
+ * Timeout for reading from socket (array(seconds, microseconds))
276
+ * @var array
277
+ */
278
+ var $_readTimeout = null;
279
+
280
+ /**
281
+ * Options to pass to Net_Socket::connect. See stream_context_create
282
+ * @var array
283
+ */
284
+ var $_socketOptions = null;
285
+ /**#@-*/
286
+
287
+ /**
288
+ * Constructor
289
+ *
290
+ * Sets up the object
291
+ * @param string The url to fetch/access
292
+ * @param array Associative array of parameters which can have the following keys:
293
+ * <ul>
294
+ * <li>method - Method to use, GET, POST etc (string)</li>
295
+ * <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
296
+ * <li>user - Basic Auth username (string)</li>
297
+ * <li>pass - Basic Auth password (string)</li>
298
+ * <li>proxy_host - Proxy server host (string)</li>
299
+ * <li>proxy_port - Proxy server port (integer)</li>
300
+ * <li>proxy_user - Proxy auth username (string)</li>
301
+ * <li>proxy_pass - Proxy auth password (string)</li>
302
+ * <li>timeout - Connection timeout in seconds (float)</li>
303
+ * <li>allowRedirects - Whether to follow redirects or not (bool)</li>
304
+ * <li>maxRedirects - Max number of redirects to follow (integer)</li>
305
+ * <li>useBrackets - Whether to append [] to array variable names (bool)</li>
306
+ * <li>saveBody - Whether to save response body in response object property (bool)</li>
307
+ * <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
308
+ * <li>socketOptions - Options to pass to Net_Socket object (array)</li>
309
+ * </ul>
310
+ * @access public
311
+ */
312
+ function HTTP_Request($url = '', $params = array())
313
+ {
314
+ $this->_method = HTTP_REQUEST_METHOD_GET;
315
+ $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
316
+ $this->_requestHeaders = array();
317
+ $this->_postData = array();
318
+ $this->_body = null;
319
+
320
+ $this->_user = null;
321
+ $this->_pass = null;
322
+
323
+ $this->_proxy_host = null;
324
+ $this->_proxy_port = null;
325
+ $this->_proxy_user = null;
326
+ $this->_proxy_pass = null;
327
+
328
+ $this->_allowRedirects = false;
329
+ $this->_maxRedirects = 3;
330
+ $this->_redirects = 0;
331
+
332
+ $this->_timeout = null;
333
+ $this->_response = null;
334
+
335
+ foreach ($params as $key => $value) {
336
+ $this->{'_' . $key} = $value;
337
+ }
338
+
339
+ if (!empty($url)) {
340
+ $this->setURL($url);
341
+ }
342
+
343
+ // Default useragent
344
+ $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
345
+
346
+ // We don't do keep-alives by default
347
+ $this->addHeader('Connection', 'close');
348
+
349
+ // Basic authentication
350
+ if (!empty($this->_user)) {
351
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
352
+ }
353
+
354
+ // Proxy authentication (see bug #5913)
355
+ if (!empty($this->_proxy_user)) {
356
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
357
+ }
358
+
359
+ // Use gzip encoding if possible
360
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
361
+ $this->addHeader('Accept-Encoding', 'gzip');
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Generates a Host header for HTTP/1.1 requests
367
+ *
368
+ * @access private
369
+ * @return string
370
+ */
371
+ function _generateHostHeader()
372
+ {
373
+ if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
374
+ $host = $this->_url->host . ':' . $this->_url->port;
375
+
376
+ } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
377
+ $host = $this->_url->host . ':' . $this->_url->port;
378
+
379
+ } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
380
+ $host = $this->_url->host . ':' . $this->_url->port;
381
+
382
+ } else {
383
+ $host = $this->_url->host;
384
+ }
385
+
386
+ return $host;
387
+ }
388
+
389
+ /**
390
+ * Resets the object to its initial state (DEPRECATED).
391
+ * Takes the same parameters as the constructor.
392
+ *
393
+ * @param string $url The url to be requested
394
+ * @param array $params Associative array of parameters
395
+ * (see constructor for details)
396
+ * @access public
397
+ * @deprecated deprecated since 1.2, call the constructor if this is necessary
398
+ */
399
+ function reset($url, $params = array())
400
+ {
401
+ $this->HTTP_Request($url, $params);
402
+ }
403
+
404
+ /**
405
+ * Sets the URL to be requested
406
+ *
407
+ * @param string The url to be requested
408
+ * @access public
409
+ */
410
+ function setURL($url)
411
+ {
412
+ $this->_url = new Net_URL($url, $this->_useBrackets);
413
+
414
+ if (!empty($this->_url->user) || !empty($this->_url->pass)) {
415
+ $this->setBasicAuth($this->_url->user, $this->_url->pass);
416
+ }
417
+
418
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
419
+ $this->addHeader('Host', $this->_generateHostHeader());
420
+ }
421
+
422
+ // set '/' instead of empty path rather than check later (see bug #8662)
423
+ if (empty($this->_url->path)) {
424
+ $this->_url->path = '/';
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Returns the current request URL
430
+ *
431
+ * @return string Current request URL
432
+ * @access public
433
+ */
434
+ function getUrl()
435
+ {
436
+ return empty($this->_url)? '': $this->_url->getUrl();
437
+ }
438
+
439
+ /**
440
+ * Sets a proxy to be used
441
+ *
442
+ * @param string Proxy host
443
+ * @param int Proxy port
444
+ * @param string Proxy username
445
+ * @param string Proxy password
446
+ * @access public
447
+ */
448
+ function setProxy($host, $port = 8080, $user = null, $pass = null)
449
+ {
450
+ $this->_proxy_host = $host;
451
+ $this->_proxy_port = $port;
452
+ $this->_proxy_user = $user;
453
+ $this->_proxy_pass = $pass;
454
+
455
+ if (!empty($user)) {
456
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Sets basic authentication parameters
462
+ *
463
+ * @param string Username
464
+ * @param string Password
465
+ */
466
+ function setBasicAuth($user, $pass)
467
+ {
468
+ $this->_user = $user;
469
+ $this->_pass = $pass;
470
+
471
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
472
+ }
473
+
474
+ /**
475
+ * Sets the method to be used, GET, POST etc.
476
+ *
477
+ * @param string Method to use. Use the defined constants for this
478
+ * @access public
479
+ */
480
+ function setMethod($method)
481
+ {
482
+ $this->_method = $method;
483
+ }
484
+
485
+ /**
486
+ * Sets the HTTP version to use, 1.0 or 1.1
487
+ *
488
+ * @param string Version to use. Use the defined constants for this
489
+ * @access public
490
+ */
491
+ function setHttpVer($http)
492
+ {
493
+ $this->_http = $http;
494
+ }
495
+
496
+ /**
497
+ * Adds a request header
498
+ *
499
+ * @param string Header name
500
+ * @param string Header value
501
+ * @access public
502
+ */
503
+ function addHeader($name, $value)
504
+ {
505
+ $this->_requestHeaders[strtolower($name)] = $value;
506
+ }
507
+
508
+ /**
509
+ * Removes a request header
510
+ *
511
+ * @param string Header name to remove
512
+ * @access public
513
+ */
514
+ function removeHeader($name)
515
+ {
516
+ if (isset($this->_requestHeaders[strtolower($name)])) {
517
+ unset($this->_requestHeaders[strtolower($name)]);
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Adds a querystring parameter
523
+ *
524
+ * @param string Querystring parameter name
525
+ * @param string Querystring parameter value
526
+ * @param bool Whether the value is already urlencoded or not, default = not
527
+ * @access public
528
+ */
529
+ function addQueryString($name, $value, $preencoded = false)
530
+ {
531
+ $this->_url->addQueryString($name, $value, $preencoded);
532
+ }
533
+
534
+ /**
535
+ * Sets the querystring to literally what you supply
536
+ *
537
+ * @param string The querystring data. Should be of the format foo=bar&x=y etc
538
+ * @param bool Whether data is already urlencoded or not, default = already encoded
539
+ * @access public
540
+ */
541
+ function addRawQueryString($querystring, $preencoded = true)
542
+ {
543
+ $this->_url->addRawQueryString($querystring, $preencoded);
544
+ }
545
+
546
+ /**
547
+ * Adds postdata items
548
+ *
549
+ * @param string Post data name
550
+ * @param string Post data value
551
+ * @param bool Whether data is already urlencoded or not, default = not
552
+ * @access public
553
+ */
554
+ function addPostData($name, $value, $preencoded = false)
555
+ {
556
+ if ($preencoded) {
557
+ $this->_postData[$name] = $value;
558
+ } else {
559
+ $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
560
+ }
561
+ }
562
+
563
+ /**
564
+ * Recursively applies the callback function to the value
565
+ *
566
+ * @param mixed Callback function
567
+ * @param mixed Value to process
568
+ * @access private
569
+ * @return mixed Processed value
570
+ */
571
+ function _arrayMapRecursive($callback, $value)
572
+ {
573
+ if (!is_array($value)) {
574
+ return call_user_func($callback, $value);
575
+ } else {
576
+ $map = array();
577
+ foreach ($value as $k => $v) {
578
+ $map[$k] = $this->_arrayMapRecursive($callback, $v);
579
+ }
580
+ return $map;
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Adds a file to form-based file upload
586
+ *
587
+ * Used to emulate file upload via a HTML form. The method also sets
588
+ * Content-Type of HTTP request to 'multipart/form-data'.
589
+ *
590
+ * If you just want to send the contents of a file as the body of HTTP
591
+ * request you should use setBody() method.
592
+ *
593
+ * @access public
594
+ * @param string name of file-upload field
595
+ * @param mixed file name(s)
596
+ * @param mixed content-type(s) of file(s) being uploaded
597
+ * @return bool true on success
598
+ * @throws PEAR_Error
599
+ */
600
+ function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
601
+ {
602
+ if (!is_array($fileName) && !is_readable($fileName)) {
603
+ return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
604
+ } elseif (is_array($fileName)) {
605
+ foreach ($fileName as $name) {
606
+ if (!is_readable($name)) {
607
+ return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
608
+ }
609
+ }
610
+ }
611
+ $this->addHeader('Content-Type', 'multipart/form-data');
612
+ $this->_postFiles[$inputName] = array(
613
+ 'name' => $fileName,
614
+ 'type' => $contentType
615
+ );
616
+ return true;
617
+ }
618
+
619
+ /**
620
+ * Adds raw postdata (DEPRECATED)
621
+ *
622
+ * @param string The data
623
+ * @param bool Whether data is preencoded or not, default = already encoded
624
+ * @access public
625
+ * @deprecated deprecated since 1.3.0, method setBody() should be used instead
626
+ */
627
+ function addRawPostData($postdata, $preencoded = true)
628
+ {
629
+ $this->_body = $preencoded ? $postdata : urlencode($postdata);
630
+ }
631
+
632
+ /**
633
+ * Sets the request body (for POST, PUT and similar requests)
634
+ *
635
+ * @param string Request body
636
+ * @access public
637
+ */
638
+ function setBody($body)
639
+ {
640
+ $this->_body = $body;
641
+ }
642
+
643
+ /**
644
+ * Clears any postdata that has been added (DEPRECATED).
645
+ *
646
+ * Useful for multiple request scenarios.
647
+ *
648
+ * @access public
649
+ * @deprecated deprecated since 1.2
650
+ */
651
+ function clearPostData()
652
+ {
653
+ $this->_postData = null;
654
+ }
655
+
656
+ /**
657
+ * Appends a cookie to "Cookie:" header
658
+ *
659
+ * @param string $name cookie name
660
+ * @param string $value cookie value
661
+ * @access public
662
+ */
663
+ function addCookie($name, $value)
664
+ {
665
+ $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
666
+ $this->addHeader('Cookie', $cookies . $name . '=' . $value);
667
+ }
668
+
669
+ /**
670
+ * Clears any cookies that have been added (DEPRECATED).
671
+ *
672
+ * Useful for multiple request scenarios
673
+ *
674
+ * @access public
675
+ * @deprecated deprecated since 1.2
676
+ */
677
+ function clearCookies()
678
+ {
679
+ $this->removeHeader('Cookie');
680
+ }
681
+
682
+ /**
683
+ * Sends the request
684
+ *
685
+ * @access public
686
+ * @param bool Whether to store response body in Response object property,
687
+ * set this to false if downloading a LARGE file and using a Listener
688
+ * @return mixed PEAR error on error, true otherwise
689
+ */
690
+ function sendRequest($saveBody = true)
691
+ {
692
+ if (!$this->_url instanceof Net_Url) {
693
+ return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
694
+ }
695
+
696
+ $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
697
+ $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
698
+
699
+ if (strcasecmp($this->_url->protocol, 'https') == 0) {
700
+ // Bug #14127, don't try connecting to HTTPS sites without OpenSSL
701
+ if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) {
702
+ return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests',
703
+ HTTP_REQUEST_ERROR_URL);
704
+ } elseif (isset($this->_proxy_host)) {
705
+ return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
706
+ }
707
+ $host = 'ssl://' . $host;
708
+ }
709
+
710
+ // magic quotes may fuck up file uploads and chunked response processing
711
+ $magicQuotes = ini_get('magic_quotes_runtime');
712
+ ini_set('magic_quotes_runtime', false);
713
+
714
+ // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
715
+ // connection token to a proxy server...
716
+ if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
717
+ 'Keep-Alive' == $this->_requestHeaders['connection'])
718
+ {
719
+ $this->removeHeader('connection');
720
+ }
721
+
722
+ $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
723
+ (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
724
+ $sockets = &PEAR::getStaticProperty('HTTP_Request', 'sockets');
725
+ $sockKey = $host . ':' . $port;
726
+ unset($this->_sock);
727
+
728
+ // There is a connected socket in the "static" property?
729
+ if ($keepAlive && !empty($sockets[$sockKey]) &&
730
+ !empty($sockets[$sockKey]->fp))
731
+ {
732
+ $this->_sock =& $sockets[$sockKey];
733
+ $err = null;
734
+ } else {
735
+ $this->_notify('connect');
736
+ $this->_sock = new Net_Socket();
737
+ $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
738
+ }
739
+ PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
740
+
741
+ if (!PEAR::isError($err)) {
742
+ if (!empty($this->_readTimeout)) {
743
+ $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
744
+ }
745
+
746
+ $this->_notify('sentRequest');
747
+
748
+ // Read the response
749
+ $this->_response = new HTTP_Response($this->_sock, $this->_listeners);
750
+ $err = $this->_response->process(
751
+ $this->_saveBody && $saveBody,
752
+ HTTP_REQUEST_METHOD_HEAD != $this->_method
753
+ );
754
+
755
+ if ($keepAlive) {
756
+ $keepAlive = (isset($this->_response->_headers['content-length'])
757
+ || (isset($this->_response->_headers['transfer-encoding'])
758
+ && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
759
+ if ($keepAlive) {
760
+ if (isset($this->_response->_headers['connection'])) {
761
+ $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
762
+ } else {
763
+ $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
764
+ }
765
+ }
766
+ }
767
+ }
768
+
769
+ ini_set('magic_quotes_runtime', $magicQuotes);
770
+
771
+ if (PEAR::isError($err)) {
772
+ return $err;
773
+ }
774
+
775
+ if (!$keepAlive) {
776
+ $this->disconnect();
777
+ // Store the connected socket in "static" property
778
+ } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
779
+ $sockets[$sockKey] =& $this->_sock;
780
+ }
781
+
782
+ // Check for redirection
783
+ if ( $this->_allowRedirects
784
+ AND $this->_redirects <= $this->_maxRedirects
785
+ AND $this->getResponseCode() > 300
786
+ AND $this->getResponseCode() < 399
787
+ AND !empty($this->_response->_headers['location'])) {
788
+
789
+
790
+ $redirect = $this->_response->_headers['location'];
791
+
792
+ // Absolute URL
793
+ if (preg_match('/^https?:\/\//i', $redirect)) {
794
+ $this->_url = new Net_URL($redirect);
795
+ $this->addHeader('Host', $this->_generateHostHeader());
796
+ // Absolute path
797
+ } elseif ($redirect{0} == '/') {
798
+ $this->_url->path = $redirect;
799
+
800
+ // Relative path
801
+ } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
802
+ if (substr($this->_url->path, -1) == '/') {
803
+ $redirect = $this->_url->path . $redirect;
804
+ } else {
805
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
806
+ }
807
+ $redirect = Net_URL::resolvePath($redirect);
808
+ $this->_url->path = $redirect;
809
+
810
+ // Filename, no path
811
+ } else {
812
+ if (substr($this->_url->path, -1) == '/') {
813
+ $redirect = $this->_url->path . $redirect;
814
+ } else {
815
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
816
+ }
817
+ $this->_url->path = $redirect;
818
+ }
819
+
820
+ $this->_redirects++;
821
+ return $this->sendRequest($saveBody);
822
+
823
+ // Too many redirects
824
+ } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
825
+ return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
826
+ }
827
+
828
+ return true;
829
+ }
830
+
831
+ /**
832
+ * Disconnect the socket, if connected. Only useful if using Keep-Alive.
833
+ *
834
+ * @access public
835
+ */
836
+ function disconnect()
837
+ {
838
+ if (!empty($this->_sock) && !empty($this->_sock->fp)) {
839
+ $this->_notify('disconnect');
840
+ $this->_sock->disconnect();
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Returns the response code
846
+ *
847
+ * @access public
848
+ * @return mixed Response code, false if not set
849
+ */
850
+ function getResponseCode()
851
+ {
852
+ return isset($this->_response->_code) ? $this->_response->_code : false;
853
+ }
854
+
855
+ /**
856
+ * Returns the response reason phrase
857
+ *
858
+ * @access public
859
+ * @return mixed Response reason phrase, false if not set
860
+ */
861
+ function getResponseReason()
862
+ {
863
+ return isset($this->_response->_reason) ? $this->_response->_reason : false;
864
+ }
865
+
866
+ /**
867
+ * Returns either the named header or all if no name given
868
+ *
869
+ * @access public
870
+ * @param string The header name to return, do not set to get all headers
871
+ * @return mixed either the value of $headername (false if header is not present)
872
+ * or an array of all headers
873
+ */
874
+ function getResponseHeader($headername = null)
875
+ {
876
+ if (!isset($headername)) {
877
+ return isset($this->_response->_headers)? $this->_response->_headers: array();
878
+ } else {
879
+ $headername = strtolower($headername);
880
+ return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
881
+ }
882
+ }
883
+
884
+ /**
885
+ * Returns the body of the response
886
+ *
887
+ * @access public
888
+ * @return mixed response body, false if not set
889
+ */
890
+ function getResponseBody()
891
+ {
892
+ return isset($this->_response->_body) ? $this->_response->_body : false;
893
+ }
894
+
895
+ /**
896
+ * Returns cookies set in response
897
+ *
898
+ * @access public
899
+ * @return mixed array of response cookies, false if none are present
900
+ */
901
+ function getResponseCookies()
902
+ {
903
+ return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
904
+ }
905
+
906
+ /**
907
+ * Builds the request string
908
+ *
909
+ * @access private
910
+ * @return string The request string
911
+ */
912
+ function _buildRequest()
913
+ {
914
+ $separator = ini_get('arg_separator.output');
915
+ ini_set('arg_separator.output', '&');
916
+ $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
917
+ ini_set('arg_separator.output', $separator);
918
+
919
+ $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
920
+ $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
921
+ $path = $this->_url->path . $querystring;
922
+ $url = $host . $port . $path;
923
+
924
+ if (!strlen($url)) {
925
+ $url = '/';
926
+ }
927
+
928
+ $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
929
+
930
+ if (in_array($this->_method, $this->_bodyDisallowed) ||
931
+ (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
932
+ (empty($this->_postData) && empty($this->_postFiles)))))
933
+ {
934
+ $this->removeHeader('Content-Type');
935
+ } else {
936
+ if (empty($this->_requestHeaders['content-type'])) {
937
+ // Add default content-type
938
+ $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
939
+ } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
940
+ $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
941
+ $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
942
+ }
943
+ }
944
+
945
+ // Request Headers
946
+ if (!empty($this->_requestHeaders)) {
947
+ foreach ($this->_requestHeaders as $name => $value) {
948
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
949
+ $request .= $canonicalName . ': ' . $value . "\r\n";
950
+ }
951
+ }
952
+
953
+ // Method does not allow a body, simply add a final CRLF
954
+ if (in_array($this->_method, $this->_bodyDisallowed)) {
955
+
956
+ $request .= "\r\n";
957
+
958
+ // Post data if it's an array
959
+ } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
960
+ (!empty($this->_postData) || !empty($this->_postFiles))) {
961
+
962
+ // "normal" POST request
963
+ if (!isset($boundary)) {
964
+ $postdata = implode('&', array_map(
965
+ create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
966
+ $this->_flattenArray('', $this->_postData)
967
+ ));
968
+
969
+ // multipart request, probably with file uploads
970
+ } else {
971
+ $postdata = '';
972
+ if (!empty($this->_postData)) {
973
+ $flatData = $this->_flattenArray('', $this->_postData);
974
+ foreach ($flatData as $item) {
975
+ $postdata .= '--' . $boundary . "\r\n";
976
+ $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
977
+ $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
978
+ }
979
+ }
980
+ foreach ($this->_postFiles as $name => $value) {
981
+ if (is_array($value['name'])) {
982
+ $varname = $name . ($this->_useBrackets? '[]': '');
983
+ } else {
984
+ $varname = $name;
985
+ $value['name'] = array($value['name']);
986
+ }
987
+ foreach ($value['name'] as $key => $filename) {
988
+ $fp = fopen($filename, 'r');
989
+ $basename = basename($filename);
990
+ $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
991
+
992
+ $postdata .= '--' . $boundary . "\r\n";
993
+ $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
994
+ $postdata .= "\r\nContent-Type: " . $type;
995
+ $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n";
996
+ fclose($fp);
997
+ }
998
+ }
999
+ $postdata .= '--' . $boundary . "--\r\n";
1000
+ }
1001
+ $request .= 'Content-Length: ' .
1002
+ (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
1003
+ "\r\n\r\n";
1004
+ $request .= $postdata;
1005
+
1006
+ // Explicitly set request body
1007
+ } elseif (0 < strlen($this->_body)) {
1008
+
1009
+ $request .= 'Content-Length: ' .
1010
+ (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
1011
+ "\r\n\r\n";
1012
+ $request .= $this->_body;
1013
+
1014
+ // No body: send a Content-Length header nonetheless (request #12900),
1015
+ // but do that only for methods that require a body (bug #14740)
1016
+ } else {
1017
+
1018
+ if (in_array($this->_method, $this->_bodyRequired)) {
1019
+ $request .= "Content-Length: 0\r\n";
1020
+ }
1021
+ $request .= "\r\n";
1022
+ }
1023
+
1024
+ return $request;
1025
+ }
1026
+
1027
+ /**
1028
+ * Helper function to change the (probably multidimensional) associative array
1029
+ * into the simple one.
1030
+ *
1031
+ * @param string name for item
1032
+ * @param mixed item's values
1033
+ * @return array array with the following items: array('item name', 'item value');
1034
+ * @access private
1035
+ */
1036
+ function _flattenArray($name, $values)
1037
+ {
1038
+ if (!is_array($values)) {
1039
+ return array(array($name, $values));
1040
+ } else {
1041
+ $ret = array();
1042
+ foreach ($values as $k => $v) {
1043
+ if (empty($name)) {
1044
+ $newName = $k;
1045
+ } elseif ($this->_useBrackets) {
1046
+ $newName = $name . '[' . $k . ']';
1047
+ } else {
1048
+ $newName = $name;
1049
+ }
1050
+ $ret = array_merge($ret, $this->_flattenArray($newName, $v));
1051
+ }
1052
+ return $ret;
1053
+ }
1054
+ }
1055
+
1056
+
1057
+ /**
1058
+ * Adds a Listener to the list of listeners that are notified of
1059
+ * the object's events
1060
+ *
1061
+ * Events sent by HTTP_Request object
1062
+ * - 'connect': on connection to server
1063
+ * - 'sentRequest': after the request was sent
1064
+ * - 'disconnect': on disconnection from server
1065
+ *
1066
+ * Events sent by HTTP_Response object
1067
+ * - 'gotHeaders': after receiving response headers (headers are passed in $data)
1068
+ * - 'tick': on receiving a part of response body (the part is passed in $data)
1069
+ * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
1070
+ * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
1071
+ *
1072
+ * @param HTTP_Request_Listener listener to attach
1073
+ * @return boolean whether the listener was successfully attached
1074
+ * @access public
1075
+ */
1076
+ function attach(&$listener)
1077
+ {
1078
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
1079
+ return false;
1080
+ }
1081
+ $this->_listeners[$listener->getId()] =& $listener;
1082
+ return true;
1083
+ }
1084
+
1085
+
1086
+ /**
1087
+ * Removes a Listener from the list of listeners
1088
+ *
1089
+ * @param HTTP_Request_Listener listener to detach
1090
+ * @return boolean whether the listener was successfully detached
1091
+ * @access public
1092
+ */
1093
+ function detach(&$listener)
1094
+ {
1095
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
1096
+ !isset($this->_listeners[$listener->getId()])) {
1097
+ return false;
1098
+ }
1099
+ unset($this->_listeners[$listener->getId()]);
1100
+ return true;
1101
+ }
1102
+
1103
+
1104
+ /**
1105
+ * Notifies all registered listeners of an event.
1106
+ *
1107
+ * @param string Event name
1108
+ * @param mixed Additional data
1109
+ * @access private
1110
+ * @see HTTP_Request::attach()
1111
+ */
1112
+ function _notify($event, $data = null)
1113
+ {
1114
+ foreach (array_keys($this->_listeners) as $id) {
1115
+ $this->_listeners[$id]->update($this, $event, $data);
1116
+ }
1117
+ }
1118
+ }
1119
+
1120
+
1121
+ /**
1122
+ * Response class to complement the Request class
1123
+ *
1124
+ * @category HTTP
1125
+ * @package HTTP_Request
1126
+ * @author Richard Heyes <richard@phpguru.org>
1127
+ * @author Alexey Borzov <avb@php.net>
1128
+ * @version Release: 1.4.4
1129
+ */
1130
+ class HTTP_Response
1131
+ {
1132
+ /**
1133
+ * Socket object
1134
+ * @var Net_Socket
1135
+ */
1136
+ var $_sock;
1137
+
1138
+ /**
1139
+ * Protocol
1140
+ * @var string
1141
+ */
1142
+ var $_protocol;
1143
+
1144
+ /**
1145
+ * Return code
1146
+ * @var string
1147
+ */
1148
+ var $_code;
1149
+
1150
+ /**
1151
+ * Response reason phrase
1152
+ * @var string
1153
+ */
1154
+ var $_reason;
1155
+
1156
+ /**
1157
+ * Response headers
1158
+ * @var array
1159
+ */
1160
+ var $_headers;
1161
+
1162
+ /**
1163
+ * Cookies set in response
1164
+ * @var array
1165
+ */
1166
+ var $_cookies;
1167
+
1168
+ /**
1169
+ * Response body
1170
+ * @var string
1171
+ */
1172
+ var $_body = '';
1173
+
1174
+ /**
1175
+ * Used by _readChunked(): remaining length of the current chunk
1176
+ * @var string
1177
+ */
1178
+ var $_chunkLength = 0;
1179
+
1180
+ /**
1181
+ * Attached listeners
1182
+ * @var array
1183
+ */
1184
+ var $_listeners = array();
1185
+
1186
+ /**
1187
+ * Bytes left to read from message-body
1188
+ * @var null|int
1189
+ */
1190
+ var $_toRead;
1191
+
1192
+ /**
1193
+ * Constructor
1194
+ *
1195
+ * @param Net_Socket socket to read the response from
1196
+ * @param array listeners attached to request
1197
+ */
1198
+ function HTTP_Response(&$sock, &$listeners)
1199
+ {
1200
+ $this->_sock =& $sock;
1201
+ $this->_listeners =& $listeners;
1202
+ }
1203
+
1204
+
1205
+ /**
1206
+ * Processes a HTTP response
1207
+ *
1208
+ * This extracts response code, headers, cookies and decodes body if it
1209
+ * was encoded in some way
1210
+ *
1211
+ * @access public
1212
+ * @param bool Whether to store response body in object property, set
1213
+ * this to false if downloading a LARGE file and using a Listener.
1214
+ * This is assumed to be true if body is gzip-encoded.
1215
+ * @param bool Whether the response can actually have a message-body.
1216
+ * Will be set to false for HEAD requests.
1217
+ * @throws PEAR_Error
1218
+ * @return mixed true on success, PEAR_Error in case of malformed response
1219
+ */
1220
+ function process($saveBody = true, $canHaveBody = true)
1221
+ {
1222
+ do {
1223
+ $line = $this->_sock->readLine();
1224
+ if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) {
1225
+ return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
1226
+ } else {
1227
+ $this->_protocol = $s[1];
1228
+ $this->_code = intval($s[2]);
1229
+ $this->_reason = empty($s[3])? null: $s[3];
1230
+ }
1231
+ while ('' !== ($header = $this->_sock->readLine())) {
1232
+ $this->_processHeader($header);
1233
+ }
1234
+ } while (100 == $this->_code);
1235
+
1236
+ $this->_notify('gotHeaders', $this->_headers);
1237
+
1238
+ // RFC 2616, section 4.4:
1239
+ // 1. Any response message which "MUST NOT" include a message-body ...
1240
+ // is always terminated by the first empty line after the header fields
1241
+ // 3. ... If a message is received with both a
1242
+ // Transfer-Encoding header field and a Content-Length header field,
1243
+ // the latter MUST be ignored.
1244
+ $canHaveBody = $canHaveBody && $this->_code >= 200 &&
1245
+ $this->_code != 204 && $this->_code != 304;
1246
+
1247
+ // If response body is present, read it and decode
1248
+ $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
1249
+ $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
1250
+ $hasBody = false;
1251
+ if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
1252
+ 0 != $this->_headers['content-length']))
1253
+ {
1254
+ if ($chunked || !isset($this->_headers['content-length'])) {
1255
+ $this->_toRead = null;
1256
+ } else {
1257
+ $this->_toRead = $this->_headers['content-length'];
1258
+ }
1259
+ while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
1260
+ if ($chunked) {
1261
+ $data = $this->_readChunked();
1262
+ } elseif (is_null($this->_toRead)) {
1263
+ $data = $this->_sock->read(4096);
1264
+ } else {
1265
+ $data = $this->_sock->read(min(4096, $this->_toRead));
1266
+ $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
1267
+ }
1268
+ if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) {
1269
+ break;
1270
+ } else {
1271
+ $hasBody = true;
1272
+ if ($saveBody || $gzipped) {
1273
+ $this->_body .= $data;
1274
+ }
1275
+ $this->_notify($gzipped? 'gzTick': 'tick', $data);
1276
+ }
1277
+ }
1278
+ }
1279
+
1280
+ if ($hasBody) {
1281
+ // Uncompress the body if needed
1282
+ if ($gzipped) {
1283
+ $body = $this->_decodeGzip($this->_body);
1284
+ if (PEAR::isError($body)) {
1285
+ return $body;
1286
+ }
1287
+ $this->_body = $body;
1288
+ $this->_notify('gotBody', $this->_body);
1289
+ } else {
1290
+ $this->_notify('gotBody');
1291
+ }
1292
+ }
1293
+ return true;
1294
+ }
1295
+
1296
+
1297
+ /**
1298
+ * Processes the response header
1299
+ *
1300
+ * @access private
1301
+ * @param string HTTP header
1302
+ */
1303
+ function _processHeader($header)
1304
+ {
1305
+ if (false === strpos($header, ':')) {
1306
+ return;
1307
+ }
1308
+ list($headername, $headervalue) = explode(':', $header, 2);
1309
+ $headername = strtolower($headername);
1310
+ $headervalue = ltrim($headervalue);
1311
+
1312
+ if ('set-cookie' != $headername) {
1313
+ if (isset($this->_headers[$headername])) {
1314
+ $this->_headers[$headername] .= ',' . $headervalue;
1315
+ } else {
1316
+ $this->_headers[$headername] = $headervalue;
1317
+ }
1318
+ } else {
1319
+ $this->_parseCookie($headervalue);
1320
+ }
1321
+ }
1322
+
1323
+
1324
+ /**
1325
+ * Parse a Set-Cookie header to fill $_cookies array
1326
+ *
1327
+ * @access private
1328
+ * @param string value of Set-Cookie header
1329
+ */
1330
+ function _parseCookie($headervalue)
1331
+ {
1332
+ $cookie = array(
1333
+ 'expires' => null,
1334
+ 'domain' => null,
1335
+ 'path' => null,
1336
+ 'secure' => false
1337
+ );
1338
+
1339
+ // Only a name=value pair
1340
+ if (!strpos($headervalue, ';')) {
1341
+ $pos = strpos($headervalue, '=');
1342
+ $cookie['name'] = trim(substr($headervalue, 0, $pos));
1343
+ $cookie['value'] = trim(substr($headervalue, $pos + 1));
1344
+
1345
+ // Some optional parameters are supplied
1346
+ } else {
1347
+ $elements = explode(';', $headervalue);
1348
+ $pos = strpos($elements[0], '=');
1349
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
1350
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
1351
+
1352
+ for ($i = 1; $i < count($elements); $i++) {
1353
+ if (false === strpos($elements[$i], '=')) {
1354
+ $elName = trim($elements[$i]);
1355
+ $elValue = null;
1356
+ } else {
1357
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
1358
+ }
1359
+ $elName = strtolower($elName);
1360
+ if ('secure' == $elName) {
1361
+ $cookie['secure'] = true;
1362
+ } elseif ('expires' == $elName) {
1363
+ $cookie['expires'] = str_replace('"', '', $elValue);
1364
+ } elseif ('path' == $elName || 'domain' == $elName) {
1365
+ $cookie[$elName] = urldecode($elValue);
1366
+ } else {
1367
+ $cookie[$elName] = $elValue;
1368
+ }
1369
+ }
1370
+ }
1371
+ $this->_cookies[] = $cookie;
1372
+ }
1373
+
1374
+
1375
+ /**
1376
+ * Read a part of response body encoded with chunked Transfer-Encoding
1377
+ *
1378
+ * @access private
1379
+ * @return string
1380
+ */
1381
+ function _readChunked()
1382
+ {
1383
+ // at start of the next chunk?
1384
+ if (0 == $this->_chunkLength) {
1385
+ $line = $this->_sock->readLine();
1386
+ if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
1387
+ $this->_chunkLength = hexdec($matches[1]);
1388
+ // Chunk with zero length indicates the end
1389
+ if (0 == $this->_chunkLength) {
1390
+ $this->_sock->readLine(); // make this an eof()
1391
+ return '';
1392
+ }
1393
+ } else {
1394
+ return '';
1395
+ }
1396
+ }
1397
+ $data = $this->_sock->read($this->_chunkLength);
1398
+ $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
1399
+ if (0 == $this->_chunkLength) {
1400
+ $this->_sock->readLine(); // Trailing CRLF
1401
+ }
1402
+ return $data;
1403
+ }
1404
+
1405
+
1406
+ /**
1407
+ * Notifies all registered listeners of an event.
1408
+ *
1409
+ * @param string Event name
1410
+ * @param mixed Additional data
1411
+ * @access private
1412
+ * @see HTTP_Request::_notify()
1413
+ */
1414
+ function _notify($event, $data = null)
1415
+ {
1416
+ foreach (array_keys($this->_listeners) as $id) {
1417
+ $this->_listeners[$id]->update($this, $event, $data);
1418
+ }
1419
+ }
1420
+
1421
+
1422
+ /**
1423
+ * Decodes the message-body encoded by gzip
1424
+ *
1425
+ * The real decoding work is done by gzinflate() built-in function, this
1426
+ * method only parses the header and checks data for compliance with
1427
+ * RFC 1952
1428
+ *
1429
+ * @access private
1430
+ * @param string gzip-encoded data
1431
+ * @return string decoded data
1432
+ */
1433
+ function _decodeGzip($data)
1434
+ {
1435
+ if (HTTP_REQUEST_MBSTRING) {
1436
+ $oldEncoding = mb_internal_encoding();
1437
+ mb_internal_encoding('iso-8859-1');
1438
+ }
1439
+ $length = strlen($data);
1440
+ // If it doesn't look like gzip-encoded data, don't bother
1441
+ if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
1442
+ return $data;
1443
+ }
1444
+ $method = ord(substr($data, 2, 1));
1445
+ if (8 != $method) {
1446
+ return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
1447
+ }
1448
+ $flags = ord(substr($data, 3, 1));
1449
+ if ($flags & 224) {
1450
+ return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
1451
+ }
1452
+
1453
+ // header is 10 bytes minimum. may be longer, though.
1454
+ $headerLength = 10;
1455
+ // extra fields, need to skip 'em
1456
+ if ($flags & 4) {
1457
+ if ($length - $headerLength - 2 < 8) {
1458
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1459
+ }
1460
+ $extraLength = unpack('v', substr($data, 10, 2));
1461
+ if ($length - $headerLength - 2 - $extraLength[1] < 8) {
1462
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1463
+ }
1464
+ $headerLength += $extraLength[1] + 2;
1465
+ }
1466
+ // file name, need to skip that
1467
+ if ($flags & 8) {
1468
+ if ($length - $headerLength - 1 < 8) {
1469
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1470
+ }
1471
+ $filenameLength = strpos(substr($data, $headerLength), chr(0));
1472
+ if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
1473
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1474
+ }
1475
+ $headerLength += $filenameLength + 1;
1476
+ }
1477
+ // comment, need to skip that also
1478
+ if ($flags & 16) {
1479
+ if ($length - $headerLength - 1 < 8) {
1480
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1481
+ }
1482
+ $commentLength = strpos(substr($data, $headerLength), chr(0));
1483
+ if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
1484
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1485
+ }
1486
+ $headerLength += $commentLength + 1;
1487
+ }
1488
+ // have a CRC for header. let's check
1489
+ if ($flags & 1) {
1490
+ if ($length - $headerLength - 2 < 8) {
1491
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
1492
+ }
1493
+ $crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
1494
+ $crcStored = unpack('v', substr($data, $headerLength, 2));
1495
+ if ($crcReal != $crcStored[1]) {
1496
+ return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
1497
+ }
1498
+ $headerLength += 2;
1499
+ }
1500
+ // unpacked data CRC and size at the end of encoded data
1501
+ $tmp = unpack('V2', substr($data, -8));
1502
+ $dataCrc = $tmp[1];
1503
+ $dataSize = $tmp[2];
1504
+
1505
+ // finally, call the gzinflate() function
1506
+ // don't pass $dataSize to gzinflate, see bugs #13135, #14370
1507
+ $unpacked = gzinflate(substr($data, $headerLength, -8));
1508
+ if (false === $unpacked) {
1509
+ return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
1510
+ } elseif ($dataSize != strlen($unpacked)) {
1511
+ return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
1512
+ } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
1513
+ return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
1514
+ }
1515
+ if (HTTP_REQUEST_MBSTRING) {
1516
+ mb_internal_encoding($oldEncoding);
1517
+ }
1518
+ return $unpacked;
1519
+ }
1520
+ } // End class HTTP_Response
1521
+ ?>
lib/PEAR/HTTP/Request/Listener.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Listener for HTTP_Request and HTTP_Response objects
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE:
8
+ *
9
+ * Copyright (c) 2002-2007, Richard Heyes
10
+ * All rights reserved.
11
+ *
12
+ * Redistribution and use in source and binary forms, with or without
13
+ * modification, are permitted provided that the following conditions
14
+ * are met:
15
+ *
16
+ * o Redistributions of source code must retain the above copyright
17
+ * notice, this list of conditions and the following disclaimer.
18
+ * o Redistributions in binary form must reproduce the above copyright
19
+ * notice, this list of conditions and the following disclaimer in the
20
+ * documentation and/or other materials provided with the distribution.
21
+ * o The names of the authors may not be used to endorse or promote
22
+ * products derived from this software without specific prior written
23
+ * 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 FOR
28
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ *
37
+ * @category HTTP
38
+ * @package HTTP_Request
39
+ * @author Alexey Borzov <avb@php.net>
40
+ * @copyright 2002-2007 Richard Heyes
41
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
42
+ * @version CVS: $Id: Listener.php,v 1.3 2007/05/18 10:33:31 avb Exp $
43
+ * @link http://pear.php.net/package/HTTP_Request/
44
+ */
45
+
46
+ /**
47
+ * Listener for HTTP_Request and HTTP_Response objects
48
+ *
49
+ * This class implements the Observer part of a Subject-Observer
50
+ * design pattern.
51
+ *
52
+ * @category HTTP
53
+ * @package HTTP_Request
54
+ * @author Alexey Borzov <avb@php.net>
55
+ * @version Release: 1.4.4
56
+ */
57
+ class HTTP_Request_Listener
58
+ {
59
+ /**
60
+ * A listener's identifier
61
+ * @var string
62
+ */
63
+ var $_id;
64
+
65
+ /**
66
+ * Constructor, sets the object's identifier
67
+ *
68
+ * @access public
69
+ */
70
+ function HTTP_Request_Listener()
71
+ {
72
+ $this->_id = md5(uniqid('http_request_', 1));
73
+ }
74
+
75
+
76
+ /**
77
+ * Returns the listener's identifier
78
+ *
79
+ * @access public
80
+ * @return string
81
+ */
82
+ function getId()
83
+ {
84
+ return $this->_id;
85
+ }
86
+
87
+
88
+ /**
89
+ * This method is called when Listener is notified of an event
90
+ *
91
+ * @access public
92
+ * @param object an object the listener is attached to
93
+ * @param string Event name
94
+ * @param mixed Additional data
95
+ * @abstract
96
+ */
97
+ function update(&$subject, $event, $data = null)
98
+ {
99
+ echo "Notified of event: '$event'\n";
100
+ if (null !== $data) {
101
+ echo "Additional data: ";
102
+ var_dump($data);
103
+ }
104
+ }
105
+ }
106
+ ?>
lib/PEAR/Mail/mime.php ADDED
@@ -0,0 +1,1095 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The Mail_Mime class is used to create MIME E-mail messages
4
+ *
5
+ * The Mail_Mime class provides an OO interface to create MIME
6
+ * enabled email messages. This way you can create emails that
7
+ * contain plain-text bodies, HTML bodies, attachments, inline
8
+ * images and specific headers.
9
+ *
10
+ * Compatible with PHP versions 4 and 5
11
+ *
12
+ * LICENSE: This LICENSE is in the BSD license style.
13
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
14
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
15
+ * All rights reserved.
16
+ *
17
+ * Redistribution and use in source and binary forms, with or
18
+ * without modification, are permitted provided that the following
19
+ * conditions are met:
20
+ *
21
+ * - Redistributions of source code must retain the above copyright
22
+ * notice, this list of conditions and the following disclaimer.
23
+ * - Redistributions in binary form must reproduce the above copyright
24
+ * notice, this list of conditions and the following disclaimer in the
25
+ * documentation and/or other materials provided with the distribution.
26
+ * - Neither the name of the authors, nor the names of its contributors
27
+ * may be used to endorse or promote products derived from this
28
+ * software without specific prior written permission.
29
+ *
30
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
40
+ * THE POSSIBILITY OF SUCH DAMAGE.
41
+ *
42
+ * @category Mail
43
+ * @package Mail_Mime
44
+ * @author Richard Heyes <richard@phpguru.org>
45
+ * @author Tomas V.V. Cox <cox@idecnet.com>
46
+ * @author Cipriano Groenendal <cipri@php.net>
47
+ * @author Sean Coates <sean@php.net>
48
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
49
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
50
+ * @version CVS: $Id: mime.php,v 1.81 2007/06/21 19:08:28 cipri Exp $
51
+ * @link http://pear.php.net/package/Mail_mime
52
+ *
53
+ * This class is based on HTML Mime Mail class from
54
+ * Richard Heyes <richard@phpguru.org> which was based also
55
+ * in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it>
56
+ * and Sascha Schumann <sascha@schumann.cx>
57
+ */
58
+
59
+
60
+ /**
61
+ * require PEAR
62
+ *
63
+ * This package depends on PEAR to raise errors.
64
+ */
65
+ require_once 'PEAR.php';
66
+
67
+ /**
68
+ * require Mail_mimePart
69
+ *
70
+ * Mail_mimePart contains the code required to
71
+ * create all the different parts a mail can
72
+ * consist of.
73
+ */
74
+ require_once 'Mail/mimePart.php';
75
+
76
+
77
+ /**
78
+ * The Mail_Mime class provides an OO interface to create MIME
79
+ * enabled email messages. This way you can create emails that
80
+ * contain plain-text bodies, HTML bodies, attachments, inline
81
+ * images and specific headers.
82
+ *
83
+ * @category Mail
84
+ * @package Mail_Mime
85
+ * @author Richard Heyes <richard@phpguru.org>
86
+ * @author Tomas V.V. Cox <cox@idecnet.com>
87
+ * @author Cipriano Groenendal <cipri@php.net>
88
+ * @author Sean Coates <sean@php.net>
89
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
90
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
91
+ * @version Release: @package_version@
92
+ * @link http://pear.php.net/package/Mail_mime
93
+ */
94
+ class Mail_mime
95
+ {
96
+ /**
97
+ * Contains the plain text part of the email
98
+ *
99
+ * @var string
100
+ * @access private
101
+ */
102
+ var $_txtbody;
103
+
104
+ /**
105
+ * Contains the html part of the email
106
+ *
107
+ * @var string
108
+ * @access private
109
+ */
110
+ var $_htmlbody;
111
+
112
+ /**
113
+ * contains the mime encoded text
114
+ *
115
+ * @var string
116
+ * @access private
117
+ */
118
+ var $_mime;
119
+
120
+ /**
121
+ * contains the multipart content
122
+ *
123
+ * @var string
124
+ * @access private
125
+ */
126
+ var $_multipart;
127
+
128
+ /**
129
+ * list of the attached images
130
+ *
131
+ * @var array
132
+ * @access private
133
+ */
134
+ var $_html_images = array();
135
+
136
+ /**
137
+ * list of the attachements
138
+ *
139
+ * @var array
140
+ * @access private
141
+ */
142
+ var $_parts = array();
143
+
144
+ /**
145
+ * Build parameters
146
+ *
147
+ * @var array
148
+ * @access private
149
+ */
150
+ var $_build_params = array();
151
+
152
+ /**
153
+ * Headers for the mail
154
+ *
155
+ * @var array
156
+ * @access private
157
+ */
158
+ var $_headers = array();
159
+
160
+ /**
161
+ * End Of Line sequence (for serialize)
162
+ *
163
+ * @var string
164
+ * @access private
165
+ */
166
+ var $_eol;
167
+
168
+
169
+ /**
170
+ * Constructor function.
171
+ *
172
+ * @param string $crlf what type of linebreak to use.
173
+ * Defaults to "\r\n"
174
+ *
175
+ * @return void
176
+ *
177
+ * @access public
178
+ */
179
+ function Mail_mime($crlf = "\r\n")
180
+ {
181
+ $this->_setEOL($crlf);
182
+ $this->_build_params = array(
183
+ 'head_encoding' => 'quoted-printable',
184
+ 'text_encoding' => '7bit',
185
+ 'html_encoding' => 'quoted-printable',
186
+ '7bit_wrap' => 998,
187
+ 'html_charset' => 'ISO-8859-1',
188
+ 'text_charset' => 'ISO-8859-1',
189
+ 'head_charset' => 'ISO-8859-1'
190
+ );
191
+ }
192
+
193
+ /**
194
+ * wakeup function called by unserialize. It re-sets the EOL constant
195
+ *
196
+ * @access private
197
+ * @return void
198
+ */
199
+ function __wakeup()
200
+ {
201
+ $this->_setEOL($this->_eol);
202
+ }
203
+
204
+
205
+ /**
206
+ * Accessor function to set the body text. Body text is used if
207
+ * it's not an html mail being sent or else is used to fill the
208
+ * text/plain part that emails clients who don't support
209
+ * html should show.
210
+ *
211
+ * @param string $data Either a string or
212
+ * the file name with the contents
213
+ * @param bool $isfile If true the first param should be treated
214
+ * as a file name, else as a string (default)
215
+ * @param bool $append If true the text or file is appended to
216
+ * the existing body, else the old body is
217
+ * overwritten
218
+ *
219
+ * @return mixed true on success or PEAR_Error object
220
+ * @access public
221
+ */
222
+ function setTXTBody($data, $isfile = false, $append = false)
223
+ {
224
+ if (!$isfile) {
225
+ if (!$append) {
226
+ $this->_txtbody = $data;
227
+ } else {
228
+ $this->_txtbody .= $data;
229
+ }
230
+ } else {
231
+ $cont = $this->_file2str($data);
232
+ if (PEAR::isError($cont)) {
233
+ return $cont;
234
+ }
235
+ if (!$append) {
236
+ $this->_txtbody = $cont;
237
+ } else {
238
+ $this->_txtbody .= $cont;
239
+ }
240
+ }
241
+ return true;
242
+ }
243
+
244
+ /**
245
+ * Adds a html part to the mail.
246
+ *
247
+ * @param string $data either a string or the file name with the
248
+ * contents
249
+ * @param bool $isfile a flag that determines whether $data is a
250
+ * filename, or a string(false, default)
251
+ *
252
+ * @return bool true on success
253
+ * @access public
254
+ */
255
+ function setHTMLBody($data, $isfile = false)
256
+ {
257
+ if (!$isfile) {
258
+ $this->_htmlbody = $data;
259
+ } else {
260
+ $cont = $this->_file2str($data);
261
+ if (PEAR::isError($cont)) {
262
+ return $cont;
263
+ }
264
+ $this->_htmlbody = $cont;
265
+ }
266
+
267
+ return true;
268
+ }
269
+
270
+ /**
271
+ * Adds an image to the list of embedded images.
272
+ *
273
+ * @param string $file the image file name OR image data itself
274
+ * @param string $c_type the content type
275
+ * @param string $name the filename of the image.
276
+ * Only used if $file is the image data.
277
+ * @param bool $isfile whether $file is a filename or not.
278
+ * Defaults to true
279
+ *
280
+ * @return bool true on success
281
+ * @access public
282
+ */
283
+ function addHTMLImage($file, $c_type='application/octet-stream',
284
+ $name = '', $isfile = true)
285
+ {
286
+ $filedata = ($isfile === true) ? $this->_file2str($file)
287
+ : $file;
288
+ if ($isfile === true) {
289
+ $filename = ($name == '' ? $file : $name);
290
+ } else {
291
+ $filename = $name;
292
+ }
293
+ if (PEAR::isError($filedata)) {
294
+ return $filedata;
295
+ }
296
+ $this->_html_images[] = array(
297
+ 'body' => $filedata,
298
+ 'name' => $filename,
299
+ 'c_type' => $c_type,
300
+ 'cid' => md5(uniqid(time()))
301
+ );
302
+ return true;
303
+ }
304
+
305
+ /**
306
+ * Adds a file to the list of attachments.
307
+ *
308
+ * @param string $file The file name of the file to attach
309
+ * OR the file contents itself
310
+ * @param string $c_type The content type
311
+ * @param string $name The filename of the attachment
312
+ * Only use if $file is the contents
313
+ * @param bool $isfile Whether $file is a filename or not
314
+ * Defaults to true
315
+ * @param string $encoding The type of encoding to use.
316
+ * Defaults to base64.
317
+ * Possible values: 7bit, 8bit, base64,
318
+ * or quoted-printable.
319
+ * @param string $disposition The content-disposition of this file
320
+ * Defaults to attachment.
321
+ * Possible values: attachment, inline.
322
+ * @param string $charset The character set used in the filename
323
+ * of this attachment.
324
+ * @param string $language The language of the attachment
325
+ * @param string $location The RFC 2557.4 location of the attachment
326
+ *
327
+ * @return mixed true on success or PEAR_Error object
328
+ * @access public
329
+ */
330
+ function addAttachment($file,
331
+ $c_type = 'application/octet-stream',
332
+ $name = '',
333
+ $isfile = true,
334
+ $encoding = 'base64',
335
+ $disposition = 'attachment',
336
+ $charset = '',
337
+ $language = '',
338
+ $location = '')
339
+ {
340
+ $filedata = ($isfile === true) ? $this->_file2str($file)
341
+ : $file;
342
+ if ($isfile === true) {
343
+ // Force the name the user supplied, otherwise use $file
344
+ $filename = (strlen($name)) ? $name : $file;
345
+ } else {
346
+ $filename = $name;
347
+ }
348
+ if (!strlen($filename)) {
349
+ $msg = "The supplied filename for the attachment can't be empty";
350
+ $err = PEAR::raiseError($msg);
351
+ return $err;
352
+ }
353
+ $filename = basename($filename);
354
+ if (PEAR::isError($filedata)) {
355
+ return $filedata;
356
+ }
357
+
358
+ $this->_parts[] = array(
359
+ 'body' => $filedata,
360
+ 'name' => $filename,
361
+ 'c_type' => $c_type,
362
+ 'encoding' => $encoding,
363
+ 'charset' => $charset,
364
+ 'language' => $language,
365
+ 'location' => $location,
366
+ 'disposition' => $disposition
367
+ );
368
+ return true;
369
+ }
370
+
371
+ /**
372
+ * Get the contents of the given file name as string
373
+ *
374
+ * @param string $file_name path of file to process
375
+ *
376
+ * @return string contents of $file_name
377
+ * @access private
378
+ */
379
+ function &_file2str($file_name)
380
+ {
381
+ if (!is_readable($file_name)) {
382
+ $err = PEAR::raiseError('File is not readable ' . $file_name);
383
+ return $err;
384
+ }
385
+ if (!$fd = fopen($file_name, 'rb')) {
386
+ $err = PEAR::raiseError('Could not open ' . $file_name);
387
+ return $err;
388
+ }
389
+ $filesize = filesize($file_name);
390
+ if ($filesize == 0) {
391
+ $cont = "";
392
+ } else {
393
+ if ($magic_quote_setting = get_magic_quotes_runtime()) {
394
+ set_magic_quotes_runtime(0);
395
+ }
396
+ $cont = fread($fd, $filesize);
397
+ if ($magic_quote_setting) {
398
+ set_magic_quotes_runtime($magic_quote_setting);
399
+ }
400
+ }
401
+ fclose($fd);
402
+ return $cont;
403
+ }
404
+
405
+ /**
406
+ * Adds a text subpart to the mimePart object and
407
+ * returns it during the build process.
408
+ *
409
+ * @param mixed &$obj The object to add the part to, or
410
+ * null if a new object is to be created.
411
+ * @param string $text The text to add.
412
+ *
413
+ * @return object The text mimePart object
414
+ * @access private
415
+ */
416
+ function &_addTextPart(&$obj, $text)
417
+ {
418
+ $params['content_type'] = 'text/plain';
419
+ $params['encoding'] = $this->_build_params['text_encoding'];
420
+ $params['charset'] = $this->_build_params['text_charset'];
421
+ if (is_object($obj)) {
422
+ $ret = $obj->addSubpart($text, $params);
423
+ return $ret;
424
+ } else {
425
+ $ret = new Mail_mimePart($text, $params);
426
+ return $ret;
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Adds a html subpart to the mimePart object and
432
+ * returns it during the build process.
433
+ *
434
+ * @param mixed &$obj The object to add the part to, or
435
+ * null if a new object is to be created.
436
+ *
437
+ * @return object The html mimePart object
438
+ * @access private
439
+ */
440
+ function &_addHtmlPart(&$obj)
441
+ {
442
+ $params['content_type'] = 'text/html';
443
+ $params['encoding'] = $this->_build_params['html_encoding'];
444
+ $params['charset'] = $this->_build_params['html_charset'];
445
+ if (is_object($obj)) {
446
+ $ret = $obj->addSubpart($this->_htmlbody, $params);
447
+ return $ret;
448
+ } else {
449
+ $ret = new Mail_mimePart($this->_htmlbody, $params);
450
+ return $ret;
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Creates a new mimePart object, using multipart/mixed as
456
+ * the initial content-type and returns it during the
457
+ * build process.
458
+ *
459
+ * @return object The multipart/mixed mimePart object
460
+ * @access private
461
+ */
462
+ function &_addMixedPart()
463
+ {
464
+ $params = array();
465
+ $params['content_type'] = 'multipart/mixed';
466
+
467
+ //Create empty multipart/mixed Mail_mimePart object to return
468
+ $ret = new Mail_mimePart('', $params);
469
+ return $ret;
470
+ }
471
+
472
+ /**
473
+ * Adds a multipart/alternative part to a mimePart
474
+ * object (or creates one), and returns it during
475
+ * the build process.
476
+ *
477
+ * @param mixed &$obj The object to add the part to, or
478
+ * null if a new object is to be created.
479
+ *
480
+ * @return object The multipart/mixed mimePart object
481
+ * @access private
482
+ */
483
+ function &_addAlternativePart(&$obj)
484
+ {
485
+ $params['content_type'] = 'multipart/alternative';
486
+ if (is_object($obj)) {
487
+ return $obj->addSubpart('', $params);
488
+ } else {
489
+ $ret = new Mail_mimePart('', $params);
490
+ return $ret;
491
+ }
492
+ }
493
+
494
+ /**
495
+ * Adds a multipart/related part to a mimePart
496
+ * object (or creates one), and returns it during
497
+ * the build process.
498
+ *
499
+ * @param mixed &$obj The object to add the part to, or
500
+ * null if a new object is to be created
501
+ *
502
+ * @return object The multipart/mixed mimePart object
503
+ * @access private
504
+ */
505
+ function &_addRelatedPart(&$obj)
506
+ {
507
+ $params['content_type'] = 'multipart/related';
508
+ if (is_object($obj)) {
509
+ return $obj->addSubpart('', $params);
510
+ } else {
511
+ $ret = new Mail_mimePart('', $params);
512
+ return $ret;
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Adds an html image subpart to a mimePart object
518
+ * and returns it during the build process.
519
+ *
520
+ * @param object &$obj The mimePart to add the image to
521
+ * @param array $value The image information
522
+ *
523
+ * @return object The image mimePart object
524
+ * @access private
525
+ */
526
+ function &_addHtmlImagePart(&$obj, $value)
527
+ {
528
+ $params['content_type'] = $value['c_type'];
529
+ $params['encoding'] = 'base64';
530
+ $params['disposition'] = 'inline';
531
+ $params['dfilename'] = $value['name'];
532
+ $params['cid'] = $value['cid'];
533
+
534
+ $ret = $obj->addSubpart($value['body'], $params);
535
+ return $ret;
536
+
537
+ }
538
+
539
+ /**
540
+ * Adds an attachment subpart to a mimePart object
541
+ * and returns it during the build process.
542
+ *
543
+ * @param object &$obj The mimePart to add the image to
544
+ * @param array $value The attachment information
545
+ *
546
+ * @return object The image mimePart object
547
+ * @access private
548
+ */
549
+ function &_addAttachmentPart(&$obj, $value)
550
+ {
551
+ $params['dfilename'] = $value['name'];
552
+ $params['encoding'] = $value['encoding'];
553
+ if ($value['charset']) {
554
+ $params['charset'] = $value['charset'];
555
+ }
556
+ if ($value['language']) {
557
+ $params['language'] = $value['language'];
558
+ }
559
+ if ($value['location']) {
560
+ $params['location'] = $value['location'];
561
+ }
562
+ $params['content_type'] = $value['c_type'];
563
+ $params['disposition'] = isset($value['disposition']) ?
564
+ $value['disposition'] : 'attachment';
565
+ $ret = $obj->addSubpart($value['body'], $params);
566
+ return $ret;
567
+ }
568
+
569
+ /**
570
+ * Returns the complete e-mail, ready to send using an alternative
571
+ * mail delivery method. Note that only the mailpart that is made
572
+ * with Mail_Mime is created. This means that,
573
+ * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF
574
+ * using the $xtra_headers parameter!
575
+ *
576
+ * @param string $separation The separation etween these two parts.
577
+ * @param array $build_params The Build parameters passed to the
578
+ * &get() function. See &get for more info.
579
+ * @param array $xtra_headers The extra headers that should be passed
580
+ * to the &headers() function.
581
+ * See that function for more info.
582
+ * @param bool $overwrite Overwrite the existing headers with new.
583
+ *
584
+ * @return string The complete e-mail.
585
+ * @access public
586
+ */
587
+ function getMessage(
588
+ $separation = null,
589
+ $build_params = null,
590
+ $xtra_headers = null,
591
+ $overwrite = false
592
+ )
593
+ {
594
+ if ($separation === null) {
595
+ $separation = MAIL_MIME_CRLF;
596
+ }
597
+ $body = $this->get($build_params);
598
+ $head = $this->txtHeaders($xtra_headers, $overwrite);
599
+ $mail = $head . $separation . $body;
600
+ return $mail;
601
+ }
602
+
603
+
604
+ /**
605
+ * Builds the multipart message from the list ($this->_parts) and
606
+ * returns the mime content.
607
+ *
608
+ * @param array $build_params Build parameters that change the way the email
609
+ * is built. Should be associative. Can contain:
610
+ * head_encoding - What encoding to use for the headers.
611
+ * Options: quoted-printable or base64
612
+ * Default is quoted-printable
613
+ * text_encoding - What encoding to use for plain text
614
+ * Options: 7bit, 8bit,
615
+ * base64, or quoted-printable
616
+ * Default is 7bit
617
+ * html_encoding - What encoding to use for html
618
+ * Options: 7bit, 8bit,
619
+ * base64, or quoted-printable
620
+ * Default is quoted-printable
621
+ * 7bit_wrap - Number of characters before text is
622
+ * wrapped in 7bit encoding
623
+ * Default is 998
624
+ * html_charset - The character set to use for html.
625
+ * Default is iso-8859-1
626
+ * text_charset - The character set to use for text.
627
+ * Default is iso-8859-1
628
+ * head_charset - The character set to use for headers.
629
+ * Default is iso-8859-1
630
+ *
631
+ * @return string The mime content
632
+ * @access public
633
+ */
634
+ function &get($build_params = null)
635
+ {
636
+ if (isset($build_params)) {
637
+ while (list($key, $value) = each($build_params)) {
638
+ $this->_build_params[$key] = $value;
639
+ }
640
+ }
641
+
642
+ if (isset($this->_headers['From'])){
643
+ $domain = @strstr($this->_headers['From'],'@');
644
+ //Bug #11381: Illegal characters in domain ID
645
+ $domain = str_replace(array("<", ">", "&", "(", ")", " ", "\"", "'"), "", $domain);
646
+ $domain = urlencode($domain);
647
+ foreach($this->_html_images as $i => $img){
648
+ $this->_html_images[$i]['cid'] = $this->_html_images[$i]['cid'] . $domain;
649
+ }
650
+ }
651
+
652
+ if (count($this->_html_images) AND isset($this->_htmlbody)) {
653
+ foreach ($this->_html_images as $key => $value) {
654
+ $regex = array();
655
+ $regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' .
656
+ preg_quote($value['name'], '#') . '\3#';
657
+ $regex[] = '#(?i)url(?-i)\(\s*(["\']?)' .
658
+ preg_quote($value['name'], '#') . '\1\s*\)#';
659
+
660
+ $rep = array();
661
+ $rep[] = '\1\2=\3cid:' . $value['cid'] .'\3';
662
+ $rep[] = 'url(\1cid:' . $value['cid'] . '\2)';
663
+
664
+ $this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody);
665
+ $this->_html_images[$key]['name'] =
666
+ basename($this->_html_images[$key]['name']);
667
+ }
668
+ }
669
+
670
+ $null = null;
671
+ $attachments = count($this->_parts) ? true : false;
672
+ $html_images = count($this->_html_images) ? true : false;
673
+ $html = strlen($this->_htmlbody) ? true : false;
674
+ $text = (!$html AND strlen($this->_txtbody)) ? true : false;
675
+
676
+ switch (true) {
677
+ case $text AND !$attachments:
678
+ $message =& $this->_addTextPart($null, $this->_txtbody);
679
+ break;
680
+
681
+ case !$text AND !$html AND $attachments:
682
+ $message =& $this->_addMixedPart();
683
+ for ($i = 0; $i < count($this->_parts); $i++) {
684
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
685
+ }
686
+ break;
687
+
688
+ case $text AND $attachments:
689
+ $message =& $this->_addMixedPart();
690
+ $this->_addTextPart($message, $this->_txtbody);
691
+ for ($i = 0; $i < count($this->_parts); $i++) {
692
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
693
+ }
694
+ break;
695
+
696
+ case $html AND !$attachments AND !$html_images:
697
+ if (isset($this->_txtbody)) {
698
+ $message =& $this->_addAlternativePart($null);
699
+ $this->_addTextPart($message, $this->_txtbody);
700
+ $this->_addHtmlPart($message);
701
+ } else {
702
+ $message =& $this->_addHtmlPart($null);
703
+ }
704
+ break;
705
+
706
+ case $html AND !$attachments AND $html_images:
707
+ $message =& $this->_addRelatedPart($null);
708
+ if (isset($this->_txtbody)) {
709
+ $alt =& $this->_addAlternativePart($message);
710
+ $this->_addTextPart($alt, $this->_txtbody);
711
+ $this->_addHtmlPart($alt);
712
+ } else {
713
+ $this->_addHtmlPart($message);
714
+ }
715
+ for ($i = 0; $i < count($this->_html_images); $i++) {
716
+ $this->_addHtmlImagePart($message, $this->_html_images[$i]);
717
+ }
718
+ break;
719
+
720
+ case $html AND $attachments AND !$html_images:
721
+ $message =& $this->_addMixedPart();
722
+ if (isset($this->_txtbody)) {
723
+ $alt =& $this->_addAlternativePart($message);
724
+ $this->_addTextPart($alt, $this->_txtbody);
725
+ $this->_addHtmlPart($alt);
726
+ } else {
727
+ $this->_addHtmlPart($message);
728
+ }
729
+ for ($i = 0; $i < count($this->_parts); $i++) {
730
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
731
+ }
732
+ break;
733
+
734
+ case $html AND $attachments AND $html_images:
735
+ $message =& $this->_addMixedPart();
736
+ if (isset($this->_txtbody)) {
737
+ $alt =& $this->_addAlternativePart($message);
738
+ $this->_addTextPart($alt, $this->_txtbody);
739
+ $rel =& $this->_addRelatedPart($alt);
740
+ } else {
741
+ $rel =& $this->_addRelatedPart($message);
742
+ }
743
+ $this->_addHtmlPart($rel);
744
+ for ($i = 0; $i < count($this->_html_images); $i++) {
745
+ $this->_addHtmlImagePart($rel, $this->_html_images[$i]);
746
+ }
747
+ for ($i = 0; $i < count($this->_parts); $i++) {
748
+ $this->_addAttachmentPart($message, $this->_parts[$i]);
749
+ }
750
+ break;
751
+
752
+ }
753
+
754
+ if (isset($message)) {
755
+ $output = $message->encode();
756
+
757
+ $this->_headers = array_merge($this->_headers,
758
+ $output['headers']);
759
+ $body = $output['body'];
760
+ return $body;
761
+
762
+ } else {
763
+ $ret = false;
764
+ return $ret;
765
+ }
766
+ }
767
+
768
+ /**
769
+ * Returns an array with the headers needed to prepend to the email
770
+ * (MIME-Version and Content-Type). Format of argument is:
771
+ * $array['header-name'] = 'header-value';
772
+ *
773
+ * @param array $xtra_headers Assoc array with any extra headers.
774
+ * Optional.
775
+ * @param bool $overwrite Overwrite already existing headers.
776
+ *
777
+ * @return array Assoc array with the mime headers
778
+ * @access public
779
+ */
780
+ function &headers($xtra_headers = null, $overwrite = false)
781
+ {
782
+ // Content-Type header should already be present,
783
+ // So just add mime version header
784
+ $headers['MIME-Version'] = '1.0';
785
+ if (isset($xtra_headers)) {
786
+ $headers = array_merge($headers, $xtra_headers);
787
+ }
788
+ if ($overwrite) {
789
+ $this->_headers = array_merge($this->_headers, $headers);
790
+ } else {
791
+ $this->_headers = array_merge($headers, $this->_headers);
792
+ }
793
+
794
+ $encodedHeaders = $this->_encodeHeaders($this->_headers);
795
+ return $encodedHeaders;
796
+ }
797
+
798
+ /**
799
+ * Get the text version of the headers
800
+ * (usefull if you want to use the PHP mail() function)
801
+ *
802
+ * @param array $xtra_headers Assoc array with any extra headers.
803
+ * Optional.
804
+ * @param bool $overwrite Overwrite the existing heaers with new.
805
+ *
806
+ * @return string Plain text headers
807
+ * @access public
808
+ */
809
+ function txtHeaders($xtra_headers = null, $overwrite = false)
810
+ {
811
+ $headers = $this->headers($xtra_headers, $overwrite);
812
+
813
+ $ret = '';
814
+ foreach ($headers as $key => $val) {
815
+ $ret .= "$key: $val" . MAIL_MIME_CRLF;
816
+ }
817
+ return $ret;
818
+ }
819
+
820
+ /**
821
+ * Sets the Subject header
822
+ *
823
+ * @param string $subject String to set the subject to.
824
+ *
825
+ * @return void
826
+ * @access public
827
+ */
828
+ function setSubject($subject)
829
+ {
830
+ $this->_headers['Subject'] = $subject;
831
+ }
832
+
833
+ /**
834
+ * Set an email to the From (the sender) header
835
+ *
836
+ * @param string $email The email address to use
837
+ *
838
+ * @return void
839
+ * @access public
840
+ */
841
+ function setFrom($email)
842
+ {
843
+ $this->_headers['From'] = $email;
844
+ }
845
+
846
+ /**
847
+ * Add an email to the Cc (carbon copy) header
848
+ * (multiple calls to this method are allowed)
849
+ *
850
+ * @param string $email The email direction to add
851
+ *
852
+ * @return void
853
+ * @access public
854
+ */
855
+ function addCc($email)
856
+ {
857
+ if (isset($this->_headers['Cc'])) {
858
+ $this->_headers['Cc'] .= ", $email";
859
+ } else {
860
+ $this->_headers['Cc'] = $email;
861
+ }
862
+ }
863
+
864
+ /**
865
+ * Add an email to the Bcc (blank carbon copy) header
866
+ * (multiple calls to this method are allowed)
867
+ *
868
+ * @param string $email The email direction to add
869
+ *
870
+ * @return void
871
+ * @access public
872
+ */
873
+ function addBcc($email)
874
+ {
875
+ if (isset($this->_headers['Bcc'])) {
876
+ $this->_headers['Bcc'] .= ", $email";
877
+ } else {
878
+ $this->_headers['Bcc'] = $email;
879
+ }
880
+ }
881
+
882
+ /**
883
+ * Since the PHP send function requires you to specifiy
884
+ * recipients (To: header) separately from the other
885
+ * headers, the To: header is not properly encoded.
886
+ * To fix this, you can use this public method to
887
+ * encode your recipients before sending to the send
888
+ * function
889
+ *
890
+ * @param string $recipients A comma-delimited list of recipients
891
+ *
892
+ * @return string Encoded data
893
+ * @access public
894
+ */
895
+ function encodeRecipients($recipients)
896
+ {
897
+ $input = array("To" => $recipients);
898
+ $retval = $this->_encodeHeaders($input);
899
+ return $retval["To"] ;
900
+ }
901
+
902
+ /**
903
+ * Encodes a header as per RFC2047
904
+ *
905
+ * @param array $input The header data to encode
906
+ * @param array $params Extra build parameters
907
+ *
908
+ * @return array Encoded data
909
+ * @access private
910
+ */
911
+ function _encodeHeaders($input, $params = array())
912
+ {
913
+
914
+ $build_params = $this->_build_params;
915
+ while (list($key, $value) = each($params)) {
916
+ $build_params[$key] = $value;
917
+ }
918
+ //$hdr_name: Name of the heaer
919
+ //$hdr_value: Full line of header value.
920
+ //$hdr_value_out: The recombined $hdr_val-atoms, or the encoded string.
921
+
922
+ $useIconv = true;
923
+ if (isset($build_params['ignore-iconv'])) {
924
+ $useIconv = !$build_params['ignore-iconv'];
925
+ }
926
+ foreach ($input as $hdr_name => $hdr_value) {
927
+ if (preg_match('#([\x80-\xFF]){1}#', $hdr_value)) {
928
+ if (function_exists('iconv_mime_encode') && $useIconv) {
929
+ $imePrefs = array();
930
+ if ($build_params['head_encoding'] == 'base64') {
931
+ $imePrefs['scheme'] = 'B';
932
+ } else {
933
+ $imePrefs['scheme'] = 'Q';
934
+ }
935
+ $imePrefs['input-charset'] = $build_params['head_charset'];
936
+ $imePrefs['output-charset'] = $build_params['head_charset'];
937
+ $imePrefs['line-length'] = 74;
938
+ $imePrefs['line-break-chars'] = "\r\n"; //Specified in RFC2047
939
+
940
+ $hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs);
941
+ $hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value);
942
+ } elseif ($build_params['head_encoding'] == 'base64') {
943
+ //Base64 encoding has been selected.
944
+ //Base64 encode the entire string
945
+ $hdr_value = base64_encode($hdr_value);
946
+
947
+ //Generate the header using the specified params and dynamicly
948
+ //determine the maximum length of such strings.
949
+ //75 is the value specified in the RFC. The first -2 is there so
950
+ //the later regexp doesn't break any of the translated chars.
951
+ //The -2 on the first line-regexp is to compensate for the ": "
952
+ //between the header-name and the header value
953
+ $prefix = '=?' . $build_params['head_charset'] . '?B?';
954
+ $suffix = '?=';
955
+ $maxLength = 75 - strlen($prefix . $suffix) - 2;
956
+ $maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
957
+
958
+ //We can cut base4 every 4 characters, so the real max
959
+ //we can get must be rounded down.
960
+ $maxLength = $maxLength - ($maxLength % 4);
961
+ $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4);
962
+
963
+ $cutpoint = $maxLength1stLine;
964
+ $hdr_value_out = $hdr_value;
965
+ $output = "";
966
+ while ($hdr_value_out) {
967
+ //Split translated string at every $maxLength
968
+ $part = substr($hdr_value_out, 0, $cutpoint);
969
+ $hdr_value_out = substr($hdr_value_out, $cutpoint);
970
+ $cutpoint = $maxLength;
971
+ //RFC 2047 specifies that any split header should
972
+ //be seperated by a CRLF SPACE.
973
+ if ($output) {
974
+ $output .= "\r\n ";
975
+ }
976
+ $output .= $prefix . $part . $suffix;
977
+ }
978
+ $hdr_value = $output;
979
+ } else {
980
+ //quoted-printable encoding has been selected
981
+
982
+ //Fix for Bug #10298, Ota Mares <om@viazenetti.de>
983
+ //Check if there is a double quote at beginning or end of
984
+ //the string to prevent that an open or closing quote gets
985
+ //ignored because it is encapsuled by an encoding pre/suffix.
986
+ //Remove the double quote and set the specific prefix or
987
+ //suffix variable so that we can concat the encoded string and
988
+ //the double quotes back together to get the intended string.
989
+ $quotePrefix = $quoteSuffix = '';
990
+ if ($hdr_value{0} == '"') {
991
+ $hdr_value = substr($hdr_value, 1);
992
+ $quotePrefix = '"';
993
+ }
994
+ if ($hdr_value{strlen($hdr_value)-1} == '"') {
995
+ $hdr_value = substr($hdr_value, 0, -1);
996
+ $quoteSuffix = '"';
997
+ }
998
+
999
+ //Generate the header using the specified params and dynamicly
1000
+ //determine the maximum length of such strings.
1001
+ //75 is the value specified in the RFC. The -2 is there so
1002
+ //the later regexp doesn't break any of the translated chars.
1003
+ //The -2 on the first line-regexp is to compensate for the ": "
1004
+ //between the header-name and the header value
1005
+ $prefix = '=?' . $build_params['head_charset'] . '?Q?';
1006
+ $suffix = '?=';
1007
+ $maxLength = 75 - strlen($prefix . $suffix) - 2 - 1;
1008
+ $maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
1009
+ $maxLength = $maxLength - 1;
1010
+
1011
+ //Replace all special characters used by the encoder.
1012
+ $search = array('=', '_', '?', ' ');
1013
+ $replace = array('=3D', '=5F', '=3F', '_');
1014
+ $hdr_value = str_replace($search, $replace, $hdr_value);
1015
+
1016
+ //Replace all extended characters (\x80-xFF) with their
1017
+ //ASCII values.
1018
+ $hdr_value = preg_replace('#([\x80-\xFF])#e',
1019
+ '"=" . strtoupper(dechex(ord("\1")))',
1020
+ $hdr_value);
1021
+
1022
+ //This regexp will break QP-encoded text at every $maxLength
1023
+ //but will not break any encoded letters.
1024
+ $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|";
1025
+ $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|";
1026
+ //Fix for Bug #10298, Ota Mares <om@viazenetti.de>
1027
+ //Concat the double quotes and encoded string together
1028
+ $hdr_value = $quotePrefix . $hdr_value . $quoteSuffix;
1029
+
1030
+
1031
+ $hdr_value_out = $hdr_value;
1032
+ $realMax = $maxLength1stLine + strlen($prefix . $suffix);
1033
+ if (strlen($hdr_value_out) >= $realMax) {
1034
+ //Begin with the regexp for the first line.
1035
+ $reg = $reg1st;
1036
+ $output = "";
1037
+ while ($hdr_value_out) {
1038
+ //Split translated string at every $maxLength
1039
+ //But make sure not to break any translated chars.
1040
+ $found = preg_match($reg, $hdr_value_out, $matches);
1041
+
1042
+ //After this first line, we need to use a different
1043
+ //regexp for the first line.
1044
+ $reg = $reg2nd;
1045
+
1046
+ //Save the found part and encapsulate it in the
1047
+ //prefix & suffix. Then remove the part from the
1048
+ //$hdr_value_out variable.
1049
+ if ($found) {
1050
+ $part = $matches[0];
1051
+ $len = strlen($matches[0]);
1052
+ $hdr_value_out = substr($hdr_value_out, $len);
1053
+ } else {
1054
+ $part = $hdr_value_out;
1055
+ $hdr_value_out = "";
1056
+ }
1057
+
1058
+ //RFC 2047 specifies that any split header should
1059
+ //be seperated by a CRLF SPACE
1060
+ if ($output) {
1061
+ $output .= "\r\n ";
1062
+ }
1063
+ $output .= $prefix . $part . $suffix;
1064
+ }
1065
+ $hdr_value_out = $output;
1066
+ } else {
1067
+ $hdr_value_out = $prefix . $hdr_value_out . $suffix;
1068
+ }
1069
+ $hdr_value = $hdr_value_out;
1070
+ }
1071
+ }
1072
+ $input[$hdr_name] = $hdr_value;
1073
+ }
1074
+ return $input;
1075
+ }
1076
+
1077
+ /**
1078
+ * Set the object's end-of-line and define the constant if applicable.
1079
+ *
1080
+ * @param string $eol End Of Line sequence
1081
+ *
1082
+ * @return void
1083
+ * @access private
1084
+ */
1085
+ function _setEOL($eol)
1086
+ {
1087
+ $this->_eol = $eol;
1088
+ if (!defined('MAIL_MIME_CRLF')) {
1089
+ define('MAIL_MIME_CRLF', $this->_eol, true);
1090
+ }
1091
+ }
1092
+
1093
+
1094
+
1095
+ } // End of class
lib/PEAR/Mail/mimeDecode.php ADDED
@@ -0,0 +1,849 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The Mail_mimeDecode class is used to decode mail/mime messages
4
+ *
5
+ * This class will parse a raw mime email and return
6
+ * the structure. Returned structure is similar to
7
+ * that returned by imap_fetchstructure().
8
+ *
9
+ * +----------------------------- IMPORTANT ------------------------------+
10
+ * | Usage of this class compared to native php extensions such as |
11
+ * | mailparse or imap, is slow and may be feature deficient. If available|
12
+ * | you are STRONGLY recommended to use the php extensions. |
13
+ * +----------------------------------------------------------------------+
14
+ *
15
+ * Compatible with PHP versions 4 and 5
16
+ *
17
+ * LICENSE: This LICENSE is in the BSD license style.
18
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
19
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
20
+ * All rights reserved.
21
+ *
22
+ * Redistribution and use in source and binary forms, with or
23
+ * without modification, are permitted provided that the following
24
+ * conditions are met:
25
+ *
26
+ * - Redistributions of source code must retain the above copyright
27
+ * notice, this list of conditions and the following disclaimer.
28
+ * - Redistributions in binary form must reproduce the above copyright
29
+ * notice, this list of conditions and the following disclaimer in the
30
+ * documentation and/or other materials provided with the distribution.
31
+ * - Neither the name of the authors, nor the names of its contributors
32
+ * may be used to endorse or promote products derived from this
33
+ * software without specific prior written permission.
34
+ *
35
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
39
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
45
+ * THE POSSIBILITY OF SUCH DAMAGE.
46
+ *
47
+ * @category Mail
48
+ * @package Mail_Mime
49
+ * @author Richard Heyes <richard@phpguru.org>
50
+ * @author George Schlossnagle <george@omniti.com>
51
+ * @author Cipriano Groenendal <cipri@php.net>
52
+ * @author Sean Coates <sean@php.net>
53
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
54
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
55
+ * @version CVS: $Id: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $
56
+ * @link http://pear.php.net/package/Mail_mime
57
+ */
58
+
59
+
60
+ /**
61
+ * require PEAR
62
+ *
63
+ * This package depends on PEAR to raise errors.
64
+ */
65
+ require_once 'PEAR.php';
66
+
67
+
68
+ /**
69
+ * The Mail_mimeDecode class is used to decode mail/mime messages
70
+ *
71
+ * This class will parse a raw mime email and return the structure.
72
+ * Returned structure is similar to that returned by imap_fetchstructure().
73
+ *
74
+ * +----------------------------- IMPORTANT ------------------------------+
75
+ * | Usage of this class compared to native php extensions such as |
76
+ * | mailparse or imap, is slow and may be feature deficient. If available|
77
+ * | you are STRONGLY recommended to use the php extensions. |
78
+ * +----------------------------------------------------------------------+
79
+ *
80
+ * @category Mail
81
+ * @package Mail_Mime
82
+ * @author Richard Heyes <richard@phpguru.org>
83
+ * @author George Schlossnagle <george@omniti.com>
84
+ * @author Cipriano Groenendal <cipri@php.net>
85
+ * @author Sean Coates <sean@php.net>
86
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
87
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
88
+ * @version Release: @package_version@
89
+ * @link http://pear.php.net/package/Mail_mime
90
+ */
91
+ class Mail_mimeDecode extends PEAR
92
+ {
93
+ /**
94
+ * The raw email to decode
95
+ *
96
+ * @var string
97
+ * @access private
98
+ */
99
+ var $_input;
100
+
101
+ /**
102
+ * The header part of the input
103
+ *
104
+ * @var string
105
+ * @access private
106
+ */
107
+ var $_header;
108
+
109
+ /**
110
+ * The body part of the input
111
+ *
112
+ * @var string
113
+ * @access private
114
+ */
115
+ var $_body;
116
+
117
+ /**
118
+ * If an error occurs, this is used to store the message
119
+ *
120
+ * @var string
121
+ * @access private
122
+ */
123
+ var $_error;
124
+
125
+ /**
126
+ * Flag to determine whether to include bodies in the
127
+ * returned object.
128
+ *
129
+ * @var boolean
130
+ * @access private
131
+ */
132
+ var $_include_bodies;
133
+
134
+ /**
135
+ * Flag to determine whether to decode bodies
136
+ *
137
+ * @var boolean
138
+ * @access private
139
+ */
140
+ var $_decode_bodies;
141
+
142
+ /**
143
+ * Flag to determine whether to decode headers
144
+ *
145
+ * @var boolean
146
+ * @access private
147
+ */
148
+ var $_decode_headers;
149
+
150
+ /**
151
+ * Constructor.
152
+ *
153
+ * Sets up the object, initialise the variables, and splits and
154
+ * stores the header and body of the input.
155
+ *
156
+ * @param string The input to decode
157
+ * @access public
158
+ */
159
+ function Mail_mimeDecode($input)
160
+ {
161
+ list($header, $body) = $this->_splitBodyHeader($input);
162
+
163
+ $this->_input = $input;
164
+ $this->_header = $header;
165
+ $this->_body = $body;
166
+ $this->_decode_bodies = false;
167
+ $this->_include_bodies = true;
168
+ }
169
+
170
+ /**
171
+ * Begins the decoding process. If called statically
172
+ * it will create an object and call the decode() method
173
+ * of it.
174
+ *
175
+ * @param array An array of various parameters that determine
176
+ * various things:
177
+ * include_bodies - Whether to include the body in the returned
178
+ * object.
179
+ * decode_bodies - Whether to decode the bodies
180
+ * of the parts. (Transfer encoding)
181
+ * decode_headers - Whether to decode headers
182
+ * input - If called statically, this will be treated
183
+ * as the input
184
+ * @return object Decoded results
185
+ * @access public
186
+ */
187
+ function decode($params = null)
188
+ {
189
+ // determine if this method has been called statically
190
+ $isStatic = !(isset($this) && get_class($this) == __CLASS__);
191
+
192
+ // Have we been called statically?
193
+ // If so, create an object and pass details to that.
194
+ if ($isStatic AND isset($params['input'])) {
195
+
196
+ $obj = new Mail_mimeDecode($params['input']);
197
+ $structure = $obj->decode($params);
198
+
199
+ // Called statically but no input
200
+ } elseif ($isStatic) {
201
+ return PEAR::raiseError('Called statically and no input given');
202
+
203
+ // Called via an object
204
+ } else {
205
+ $this->_include_bodies = isset($params['include_bodies']) ?
206
+ $params['include_bodies'] : false;
207
+ $this->_decode_bodies = isset($params['decode_bodies']) ?
208
+ $params['decode_bodies'] : false;
209
+ $this->_decode_headers = isset($params['decode_headers']) ?
210
+ $params['decode_headers'] : false;
211
+
212
+ $structure = $this->_decode($this->_header, $this->_body);
213
+ if ($structure === false) {
214
+ $structure = $this->raiseError($this->_error);
215
+ }
216
+ }
217
+
218
+ return $structure;
219
+ }
220
+
221
+ /**
222
+ * Performs the decoding. Decodes the body string passed to it
223
+ * If it finds certain content-types it will call itself in a
224
+ * recursive fashion
225
+ *
226
+ * @param string Header section
227
+ * @param string Body section
228
+ * @return object Results of decoding process
229
+ * @access private
230
+ */
231
+ function _decode($headers, $body, $default_ctype = 'text/plain')
232
+ {
233
+ $return = new stdClass;
234
+ $return->headers = array();
235
+ $headers = $this->_parseHeaders($headers);
236
+
237
+ foreach ($headers as $value) {
238
+ if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
239
+ $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]);
240
+ $return->headers[strtolower($value['name'])][] = $value['value'];
241
+
242
+ } elseif (isset($return->headers[strtolower($value['name'])])) {
243
+ $return->headers[strtolower($value['name'])][] = $value['value'];
244
+
245
+ } else {
246
+ $return->headers[strtolower($value['name'])] = $value['value'];
247
+ }
248
+ }
249
+
250
+ reset($headers);
251
+ while (list($key, $value) = each($headers)) {
252
+ $headers[$key]['name'] = strtolower($headers[$key]['name']);
253
+ switch ($headers[$key]['name']) {
254
+
255
+ case 'content-type':
256
+ $content_type = $this->_parseHeaderValue($headers[$key]['value']);
257
+
258
+ if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
259
+ $return->ctype_primary = $regs[1];
260
+ $return->ctype_secondary = $regs[2];
261
+ }
262
+
263
+ if (isset($content_type['other'])) {
264
+ while (list($p_name, $p_value) = each($content_type['other'])) {
265
+ $return->ctype_parameters[$p_name] = $p_value;
266
+ }
267
+ }
268
+ break;
269
+
270
+ case 'content-disposition':
271
+ $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
272
+ $return->disposition = $content_disposition['value'];
273
+ if (isset($content_disposition['other'])) {
274
+ while (list($p_name, $p_value) = each($content_disposition['other'])) {
275
+ $return->d_parameters[$p_name] = $p_value;
276
+ }
277
+ }
278
+ break;
279
+
280
+ case 'content-transfer-encoding':
281
+ $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
282
+ break;
283
+ }
284
+ }
285
+
286
+ if (isset($content_type)) {
287
+ switch (strtolower($content_type['value'])) {
288
+ case 'text/plain':
289
+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
290
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
291
+ break;
292
+
293
+ case 'text/html':
294
+ $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
295
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
296
+ break;
297
+
298
+ case 'multipart/parallel':
299
+ case 'multipart/appledouble': // Appledouble mail
300
+ case 'multipart/report': // RFC1892
301
+ case 'multipart/signed': // PGP
302
+ case 'multipart/digest':
303
+ case 'multipart/alternative':
304
+ case 'multipart/related':
305
+ case 'multipart/mixed':
306
+ if(!isset($content_type['other']['boundary'])){
307
+ $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
308
+ return false;
309
+ }
310
+
311
+ $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
312
+
313
+ $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
314
+ for ($i = 0; $i < count($parts); $i++) {
315
+ list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
316
+ $part = $this->_decode($part_header, $part_body, $default_ctype);
317
+ if($part === false)
318
+ $part = $this->raiseError($this->_error);
319
+ $return->parts[] = $part;
320
+ }
321
+ break;
322
+
323
+ case 'message/rfc822':
324
+ $obj = new Mail_mimeDecode($body);
325
+ $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
326
+ 'decode_bodies' => $this->_decode_bodies,
327
+ 'decode_headers' => $this->_decode_headers));
328
+ unset($obj);
329
+ break;
330
+
331
+ default:
332
+ if(!isset($content_transfer_encoding['value']))
333
+ $content_transfer_encoding['value'] = '7bit';
334
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
335
+ break;
336
+ }
337
+
338
+ } else {
339
+ $ctype = explode('/', $default_ctype);
340
+ $return->ctype_primary = $ctype[0];
341
+ $return->ctype_secondary = $ctype[1];
342
+ $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
343
+ }
344
+
345
+ return $return;
346
+ }
347
+
348
+ /**
349
+ * Given the output of the above function, this will return an
350
+ * array of references to the parts, indexed by mime number.
351
+ *
352
+ * @param object $structure The structure to go through
353
+ * @param string $mime_number Internal use only.
354
+ * @return array Mime numbers
355
+ */
356
+ function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
357
+ {
358
+ $return = array();
359
+ if (!empty($structure->parts)) {
360
+ if ($mime_number != '') {
361
+ $structure->mime_id = $prepend . $mime_number;
362
+ $return[$prepend . $mime_number] = &$structure;
363
+ }
364
+ for ($i = 0; $i < count($structure->parts); $i++) {
365
+
366
+
367
+ if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
368
+ $prepend = $prepend . $mime_number . '.';
369
+ $_mime_number = '';
370
+ } else {
371
+ $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
372
+ }
373
+
374
+ $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
375
+ foreach ($arr as $key => $val) {
376
+ $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
377
+ }
378
+ }
379
+ } else {
380
+ if ($mime_number == '') {
381
+ $mime_number = '1';
382
+ }
383
+ $structure->mime_id = $prepend . $mime_number;
384
+ $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
385
+ }
386
+
387
+ return $return;
388
+ }
389
+
390
+ /**
391
+ * Given a string containing a header and body
392
+ * section, this function will split them (at the first
393
+ * blank line) and return them.
394
+ *
395
+ * @param string Input to split apart
396
+ * @return array Contains header and body section
397
+ * @access private
398
+ */
399
+ function _splitBodyHeader($input)
400
+ {
401
+ if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
402
+ return array($match[1], $match[2]);
403
+ }
404
+ $this->_error = 'Could not split header and body';
405
+ return false;
406
+ }
407
+
408
+ /**
409
+ * Parse headers given in $input and return
410
+ * as assoc array.
411
+ *
412
+ * @param string Headers to parse
413
+ * @return array Contains parsed headers
414
+ * @access private
415
+ */
416
+ function _parseHeaders($input)
417
+ {
418
+
419
+ if ($input !== '') {
420
+ // Unfold the input
421
+ $input = preg_replace("/\r?\n/", "\r\n", $input);
422
+ $input = preg_replace("/\r\n(\t| )+/", ' ', $input);
423
+ $headers = explode("\r\n", trim($input));
424
+
425
+ foreach ($headers as $value) {
426
+ $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
427
+ $hdr_value = substr($value, $pos+1);
428
+ if($hdr_value[0] == ' ')
429
+ $hdr_value = substr($hdr_value, 1);
430
+
431
+ $return[] = array(
432
+ 'name' => $hdr_name,
433
+ 'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
434
+ );
435
+ }
436
+ } else {
437
+ $return = array();
438
+ }
439
+
440
+ return $return;
441
+ }
442
+
443
+ /**
444
+ * Function to parse a header value,
445
+ * extract first part, and any secondary
446
+ * parts (after ;) This function is not as
447
+ * robust as it could be. Eg. header comments
448
+ * in the wrong place will probably break it.
449
+ *
450
+ * @param string Header value to parse
451
+ * @return array Contains parsed result
452
+ * @access private
453
+ */
454
+ function _parseHeaderValue($input)
455
+ {
456
+
457
+ if (($pos = strpos($input, ';')) !== false) {
458
+
459
+ $return['value'] = trim(substr($input, 0, $pos));
460
+ $input = trim(substr($input, $pos+1));
461
+
462
+ if (strlen($input) > 0) {
463
+
464
+ // This splits on a semi-colon, if there's no preceeding backslash
465
+ // Now works with quoted values; had to glue the \; breaks in PHP
466
+ // the regex is already bordering on incomprehensible
467
+ $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
468
+ preg_match_all($splitRegex, $input, $matches);
469
+ $parameters = array();
470
+ for ($i=0; $i<count($matches[0]); $i++) {
471
+ $param = $matches[0][$i];
472
+ while (substr($param, -2) == '\;') {
473
+ $param .= $matches[0][++$i];
474
+ }
475
+ $parameters[] = $param;
476
+ }
477
+
478
+ for ($i = 0; $i < count($parameters); $i++) {
479
+ $param_name = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
480
+ $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
481
+ if ($param_value[0] == '"') {
482
+ $param_value = substr($param_value, 1, -1);
483
+ }
484
+ $return['other'][$param_name] = $param_value;
485
+ $return['other'][strtolower($param_name)] = $param_value;
486
+ }
487
+ }
488
+ } else {
489
+ $return['value'] = trim($input);
490
+ }
491
+
492
+ return $return;
493
+ }
494
+
495
+ /**
496
+ * This function splits the input based
497
+ * on the given boundary
498
+ *
499
+ * @param string Input to parse
500
+ * @return array Contains array of resulting mime parts
501
+ * @access private
502
+ */
503
+ function _boundarySplit($input, $boundary)
504
+ {
505
+ $parts = array();
506
+
507
+ $bs_possible = substr($boundary, 2, -2);
508
+ $bs_check = '\"' . $bs_possible . '\"';
509
+
510
+ if ($boundary == $bs_check) {
511
+ $boundary = $bs_possible;
512
+ }
513
+
514
+ $tmp = explode('--' . $boundary, $input);
515
+
516
+ for ($i = 1; $i < count($tmp) - 1; $i++) {
517
+ $parts[] = $tmp[$i];
518
+ }
519
+
520
+ return $parts;
521
+ }
522
+
523
+ /**
524
+ * Given a header, this function will decode it
525
+ * according to RFC2047. Probably not *exactly*
526
+ * conformant, but it does pass all the given
527
+ * examples (in RFC2047).
528
+ *
529
+ * @param string Input header value to decode
530
+ * @return string Decoded header value
531
+ * @access private
532
+ */
533
+ function _decodeHeader($input)
534
+ {
535
+ // Remove white space between encoded-words
536
+ $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
537
+
538
+ // For each encoded-word...
539
+ while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
540
+
541
+ $encoded = $matches[1];
542
+ $charset = $matches[2];
543
+ $encoding = $matches[3];
544
+ $text = $matches[4];
545
+
546
+ switch (strtolower($encoding)) {
547
+ case 'b':
548
+ $text = base64_decode($text);
549
+ break;
550
+
551
+ case 'q':
552
+ $text = str_replace('_', ' ', $text);
553
+ preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
554
+ foreach($matches[1] as $value)
555
+ $text = str_replace('='.$value, chr(hexdec($value)), $text);
556
+ break;
557
+ }
558
+
559
+ $input = str_replace($encoded, $text, $input);
560
+ }
561
+
562
+ return $input;
563
+ }
564
+
565
+ /**
566
+ * Given a body string and an encoding type,
567
+ * this function will decode and return it.
568
+ *
569
+ * @param string Input body to decode
570
+ * @param string Encoding type to use.
571
+ * @return string Decoded body
572
+ * @access private
573
+ */
574
+ function _decodeBody($input, $encoding = '7bit')
575
+ {
576
+ switch (strtolower($encoding)) {
577
+ case '7bit':
578
+ return $input;
579
+ break;
580
+
581
+ case 'quoted-printable':
582
+ return $this->_quotedPrintableDecode($input);
583
+ break;
584
+
585
+ case 'base64':
586
+ return base64_decode($input);
587
+ break;
588
+
589
+ default:
590
+ return $input;
591
+ }
592
+ }
593
+
594
+ /**
595
+ * Given a quoted-printable string, this
596
+ * function will decode and return it.
597
+ *
598
+ * @param string Input body to decode
599
+ * @return string Decoded body
600
+ * @access private
601
+ */
602
+ function _quotedPrintableDecode($input)
603
+ {
604
+ // Remove soft line breaks
605
+ $input = preg_replace("/=\r?\n/", '', $input);
606
+
607
+ // Replace encoded characters
608
+ $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
609
+
610
+ return $input;
611
+ }
612
+
613
+ /**
614
+ * Checks the input for uuencoded files and returns
615
+ * an array of them. Can be called statically, eg:
616
+ *
617
+ * $files =& Mail_mimeDecode::uudecode($some_text);
618
+ *
619
+ * It will check for the begin 666 ... end syntax
620
+ * however and won't just blindly decode whatever you
621
+ * pass it.
622
+ *
623
+ * @param string Input body to look for attahcments in
624
+ * @return array Decoded bodies, filenames and permissions
625
+ * @access public
626
+ * @author Unknown
627
+ */
628
+ function &uudecode($input)
629
+ {
630
+ // Find all uuencoded sections
631
+ preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
632
+
633
+ for ($j = 0; $j < count($matches[3]); $j++) {
634
+
635
+ $str = $matches[3][$j];
636
+ $filename = $matches[2][$j];
637
+ $fileperm = $matches[1][$j];
638
+
639
+ $file = '';
640
+ $str = preg_split("/\r?\n/", trim($str));
641
+ $strlen = count($str);
642
+
643
+ for ($i = 0; $i < $strlen; $i++) {
644
+ $pos = 1;
645
+ $d = 0;
646
+ $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
647
+
648
+ while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
649
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
650
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
651
+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
652
+ $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
653
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
654
+
655
+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
656
+
657
+ $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077));
658
+
659
+ $pos += 4;
660
+ $d += 3;
661
+ }
662
+
663
+ if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
664
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
665
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
666
+ $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
667
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
668
+
669
+ $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
670
+
671
+ $pos += 3;
672
+ $d += 2;
673
+ }
674
+
675
+ if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
676
+ $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
677
+ $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
678
+ $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
679
+
680
+ }
681
+ }
682
+ $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
683
+ }
684
+
685
+ return $files;
686
+ }
687
+
688
+ /**
689
+ * getSendArray() returns the arguments required for Mail::send()
690
+ * used to build the arguments for a mail::send() call
691
+ *
692
+ * Usage:
693
+ * $mailtext = Full email (for example generated by a template)
694
+ * $decoder = new Mail_mimeDecode($mailtext);
695
+ * $parts = $decoder->getSendArray();
696
+ * if (!PEAR::isError($parts) {
697
+ * list($recipents,$headers,$body) = $parts;
698
+ * $mail = Mail::factory('smtp');
699
+ * $mail->send($recipents,$headers,$body);
700
+ * } else {
701
+ * echo $parts->message;
702
+ * }
703
+ * @return mixed array of recipeint, headers,body or Pear_Error
704
+ * @access public
705
+ * @author Alan Knowles <alan@akbkhome.com>
706
+ */
707
+ function getSendArray()
708
+ {
709
+ // prevent warning if this is not set
710
+ $this->_decode_headers = FALSE;
711
+ $headerlist =$this->_parseHeaders($this->_header);
712
+ $to = "";
713
+ if (!$headerlist) {
714
+ return $this->raiseError("Message did not contain headers");
715
+ }
716
+ foreach($headerlist as $item) {
717
+ $header[$item['name']] = $item['value'];
718
+ switch (strtolower($item['name'])) {
719
+ case "to":
720
+ case "cc":
721
+ case "bcc":
722
+ $to = ",".$item['value'];
723
+ default:
724
+ break;
725
+ }
726
+ }
727
+ if ($to == "") {
728
+ return $this->raiseError("Message did not contain any recipents");
729
+ }
730
+ $to = substr($to,1);
731
+ return array($to,$header,$this->_body);
732
+ }
733
+
734
+ /**
735
+ * Returns a xml copy of the output of
736
+ * Mail_mimeDecode::decode. Pass the output in as the
737
+ * argument. This function can be called statically. Eg:
738
+ *
739
+ * $output = $obj->decode();
740
+ * $xml = Mail_mimeDecode::getXML($output);
741
+ *
742
+ * The DTD used for this should have been in the package. Or
743
+ * alternatively you can get it from cvs, or here:
744
+ * http://www.phpguru.org/xmail/xmail.dtd.
745
+ *
746
+ * @param object Input to convert to xml. This should be the
747
+ * output of the Mail_mimeDecode::decode function
748
+ * @return string XML version of input
749
+ * @access public
750
+ */
751
+ function getXML($input)
752
+ {
753
+ $crlf = "\r\n";
754
+ $output = '<?xml version=\'1.0\'?>' . $crlf .
755
+ '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
756
+ '<email>' . $crlf .
757
+ Mail_mimeDecode::_getXML($input) .
758
+ '</email>';
759
+
760
+ return $output;
761
+ }
762
+
763
+ /**
764
+ * Function that does the actual conversion to xml. Does a single
765
+ * mimepart at a time.
766
+ *
767
+ * @param object Input to convert to xml. This is a mimepart object.
768
+ * It may or may not contain subparts.
769
+ * @param integer Number of tabs to indent
770
+ * @return string XML version of input
771
+ * @access private
772
+ */
773
+ function _getXML($input, $indent = 1)
774
+ {
775
+ $htab = "\t";
776
+ $crlf = "\r\n";
777
+ $output = '';
778
+ $headers = @(array)$input->headers;
779
+
780
+ foreach ($headers as $hdr_name => $hdr_value) {
781
+
782
+ // Multiple headers with this name
783
+ if (is_array($headers[$hdr_name])) {
784
+ for ($i = 0; $i < count($hdr_value); $i++) {
785
+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
786
+ }
787
+
788
+ // Only one header of this sort
789
+ } else {
790
+ $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
791
+ }
792
+ }
793
+
794
+ if (!empty($input->parts)) {
795
+ for ($i = 0; $i < count($input->parts); $i++) {
796
+ $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
797
+ Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
798
+ str_repeat($htab, $indent) . '</mimepart>' . $crlf;
799
+ }
800
+ } elseif (isset($input->body)) {
801
+ $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
802
+ $input->body . ']]></body>' . $crlf;
803
+ }
804
+
805
+ return $output;
806
+ }
807
+
808
+ /**
809
+ * Helper function to _getXML(). Returns xml of a header.
810
+ *
811
+ * @param string Name of header
812
+ * @param string Value of header
813
+ * @param integer Number of tabs to indent
814
+ * @return string XML version of input
815
+ * @access private
816
+ */
817
+ function _getXML_helper($hdr_name, $hdr_value, $indent)
818
+ {
819
+ $htab = "\t";
820
+ $crlf = "\r\n";
821
+ $return = '';
822
+
823
+ $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
824
+ $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
825
+
826
+ // Sort out any parameters
827
+ if (!empty($new_hdr_value['other'])) {
828
+ foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
829
+ $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
830
+ str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
831
+ str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
832
+ str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
833
+ }
834
+
835
+ $params = implode('', $params);
836
+ } else {
837
+ $params = '';
838
+ }
839
+
840
+ $return = str_repeat($htab, $indent) . '<header>' . $crlf .
841
+ str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
842
+ str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
843
+ $params .
844
+ str_repeat($htab, $indent) . '</header>' . $crlf;
845
+
846
+ return $return;
847
+ }
848
+
849
+ } // End of class
lib/PEAR/Mail/mimePart.php ADDED
@@ -0,0 +1,439 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The Mail_mimePart class is used to create MIME E-mail messages
4
+ *
5
+ * This class enables you to manipulate and build a mime email
6
+ * from the ground up. The Mail_Mime class is a userfriendly api
7
+ * to this class for people who aren't interested in the internals
8
+ * of mime mail.
9
+ * This class however allows full control over the email.
10
+ *
11
+ * Compatible with PHP versions 4 and 5
12
+ *
13
+ * LICENSE: This LICENSE is in the BSD license style.
14
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
15
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
16
+ * All rights reserved.
17
+ *
18
+ * Redistribution and use in source and binary forms, with or
19
+ * without modification, are permitted provided that the following
20
+ * conditions are met:
21
+ *
22
+ * - Redistributions of source code must retain the above copyright
23
+ * notice, this list of conditions and the following disclaimer.
24
+ * - Redistributions in binary form must reproduce the above copyright
25
+ * notice, this list of conditions and the following disclaimer in the
26
+ * documentation and/or other materials provided with the distribution.
27
+ * - Neither the name of the authors, nor the names of its contributors
28
+ * may be used to endorse or promote products derived from this
29
+ * software without specific prior written permission.
30
+ *
31
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
35
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
39
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
41
+ * THE POSSIBILITY OF SUCH DAMAGE.
42
+ *
43
+ * @category Mail
44
+ * @package Mail_Mime
45
+ * @author Richard Heyes <richard@phpguru.org>
46
+ * @author Cipriano Groenendal <cipri@php.net>
47
+ * @author Sean Coates <sean@php.net>
48
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
49
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
50
+ * @version CVS: $Id: mimePart.php,v 1.25 2007/05/14 21:43:08 cipri Exp $
51
+ * @link http://pear.php.net/package/Mail_mime
52
+ */
53
+
54
+
55
+ /**
56
+ * The Mail_mimePart class is used to create MIME E-mail messages
57
+ *
58
+ * This class enables you to manipulate and build a mime email
59
+ * from the ground up. The Mail_Mime class is a userfriendly api
60
+ * to this class for people who aren't interested in the internals
61
+ * of mime mail.
62
+ * This class however allows full control over the email.
63
+ *
64
+ * @category Mail
65
+ * @package Mail_Mime
66
+ * @author Richard Heyes <richard@phpguru.org>
67
+ * @author Cipriano Groenendal <cipri@php.net>
68
+ * @author Sean Coates <sean@php.net>
69
+ * @copyright 2003-2006 PEAR <pear-group@php.net>
70
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
71
+ * @version Release: @package_version@
72
+ * @link http://pear.php.net/package/Mail_mime
73
+ */
74
+ class Mail_mimePart {
75
+
76
+ /**
77
+ * The encoding type of this part
78
+ *
79
+ * @var string
80
+ * @access private
81
+ */
82
+ var $_encoding;
83
+
84
+ /**
85
+ * An array of subparts
86
+ *
87
+ * @var array
88
+ * @access private
89
+ */
90
+ var $_subparts;
91
+
92
+ /**
93
+ * The output of this part after being built
94
+ *
95
+ * @var string
96
+ * @access private
97
+ */
98
+ var $_encoded;
99
+
100
+ /**
101
+ * Headers for this part
102
+ *
103
+ * @var array
104
+ * @access private
105
+ */
106
+ var $_headers;
107
+
108
+ /**
109
+ * The body of this part (not encoded)
110
+ *
111
+ * @var string
112
+ * @access private
113
+ */
114
+ var $_body;
115
+
116
+ /**
117
+ * Constructor.
118
+ *
119
+ * Sets up the object.
120
+ *
121
+ * @param $body - The body of the mime part if any.
122
+ * @param $params - An associative array of parameters:
123
+ * content_type - The content type for this part eg multipart/mixed
124
+ * encoding - The encoding to use, 7bit, 8bit, base64, or quoted-printable
125
+ * cid - Content ID to apply
126
+ * disposition - Content disposition, inline or attachment
127
+ * dfilename - Optional filename parameter for content disposition
128
+ * description - Content description
129
+ * charset - Character set to use
130
+ * @access public
131
+ */
132
+ function Mail_mimePart($body = '', $params = array())
133
+ {
134
+ if (!defined('MAIL_MIMEPART_CRLF')) {
135
+ define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE);
136
+ }
137
+
138
+ $contentType = array();
139
+ $contentDisp = array();
140
+ foreach ($params as $key => $value) {
141
+ switch ($key) {
142
+ case 'content_type':
143
+ $contentType['type'] = $value;
144
+ //$headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : '');
145
+ break;
146
+
147
+ case 'encoding':
148
+ $this->_encoding = $value;
149
+ $headers['Content-Transfer-Encoding'] = $value;
150
+ break;
151
+
152
+ case 'cid':
153
+ $headers['Content-ID'] = '<' . $value . '>';
154
+ break;
155
+
156
+ case 'disposition':
157
+ $contentDisp['disp'] = $value;
158
+ break;
159
+
160
+ case 'dfilename':
161
+ $contentDisp['filename'] = $value;
162
+ $contentType['name'] = $value;
163
+ break;
164
+
165
+ case 'description':
166
+ $headers['Content-Description'] = $value;
167
+ break;
168
+
169
+ case 'charset':
170
+ $contentType['charset'] = $value;
171
+ $contentDisp['charset'] = $value;
172
+ break;
173
+
174
+ case 'language':
175
+ $contentType['language'] = $value;
176
+ $contentDisp['language'] = $value;
177
+ break;
178
+
179
+ case 'location':
180
+ $headers['Content-Location'] = $value;
181
+ break;
182
+
183
+ }
184
+ }
185
+ if (isset($contentType['type'])) {
186
+ $headers['Content-Type'] = $contentType['type'];
187
+ if (isset($contentType['name'])) {
188
+ $headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF;
189
+ $headers['Content-Type'] .= $this->_buildHeaderParam('name', $contentType['name'],
190
+ isset($contentType['charset']) ? $contentType['charset'] : 'US-ASCII',
191
+ isset($contentType['language']) ? $contentType['language'] : NULL);
192
+ } elseif (isset($contentType['charset'])) {
193
+ $headers['Content-Type'] .= "; charset=\"{$contentType['charset']}\"";
194
+ }
195
+ }
196
+
197
+
198
+ if (isset($contentDisp['disp'])) {
199
+ $headers['Content-Disposition'] = $contentDisp['disp'];
200
+ if (isset($contentDisp['filename'])) {
201
+ $headers['Content-Disposition'] .= ';' . MAIL_MIMEPART_CRLF;
202
+ $headers['Content-Disposition'] .= $this->_buildHeaderParam('filename', $contentDisp['filename'],
203
+ isset($contentDisp['charset']) ? $contentDisp['charset'] : 'US-ASCII',
204
+ isset($contentDisp['language']) ? $contentDisp['language'] : NULL);
205
+ }
206
+ }
207
+
208
+
209
+
210
+
211
+ // Default content-type
212
+ if (!isset($headers['Content-Type'])) {
213
+ $headers['Content-Type'] = 'text/plain';
214
+ }
215
+
216
+ //Default encoding
217
+ if (!isset($this->_encoding)) {
218
+ $this->_encoding = '7bit';
219
+ }
220
+
221
+ // Assign stuff to member variables
222
+ $this->_encoded = array();
223
+ $this->_headers = $headers;
224
+ $this->_body = $body;
225
+ }
226
+
227
+ /**
228
+ * encode()
229
+ *
230
+ * Encodes and returns the email. Also stores
231
+ * it in the encoded member variable
232
+ *
233
+ * @return An associative array containing two elements,
234
+ * body and headers. The headers element is itself
235
+ * an indexed array.
236
+ * @access public
237
+ */
238
+ function encode()
239
+ {
240
+ $encoded =& $this->_encoded;
241
+
242
+ if (count($this->_subparts)) {
243
+ srand((double)microtime()*1000000);
244
+ $boundary = '=_' . md5(rand() . microtime());
245
+ $this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"';
246
+
247
+ // Add body parts to $subparts
248
+ for ($i = 0; $i < count($this->_subparts); $i++) {
249
+ $headers = array();
250
+ $tmp = $this->_subparts[$i]->encode();
251
+ foreach ($tmp['headers'] as $key => $value) {
252
+ $headers[] = $key . ': ' . $value;
253
+ }
254
+ $subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body'] . MAIL_MIMEPART_CRLF;
255
+ }
256
+
257
+ $encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF .
258
+ rtrim(implode('--' . $boundary . MAIL_MIMEPART_CRLF , $subparts), MAIL_MIMEPART_CRLF) . MAIL_MIMEPART_CRLF .
259
+ '--' . $boundary.'--' . MAIL_MIMEPART_CRLF;
260
+
261
+ } else {
262
+ $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding);
263
+ }
264
+
265
+ // Add headers to $encoded
266
+ $encoded['headers'] =& $this->_headers;
267
+
268
+ return $encoded;
269
+ }
270
+
271
+ /**
272
+ * &addSubPart()
273
+ *
274
+ * Adds a subpart to current mime part and returns
275
+ * a reference to it
276
+ *
277
+ * @param $body The body of the subpart, if any.
278
+ * @param $params The parameters for the subpart, same
279
+ * as the $params argument for constructor.
280
+ * @return A reference to the part you just added. It is
281
+ * crucial if using multipart/* in your subparts that
282
+ * you use =& in your script when calling this function,
283
+ * otherwise you will not be able to add further subparts.
284
+ * @access public
285
+ */
286
+ function &addSubPart($body, $params)
287
+ {
288
+ $this->_subparts[] = new Mail_mimePart($body, $params);
289
+ return $this->_subparts[count($this->_subparts) - 1];
290
+ }
291
+
292
+ /**
293
+ * _getEncodedData()
294
+ *
295
+ * Returns encoded data based upon encoding passed to it
296
+ *
297
+ * @param $data The data to encode.
298
+ * @param $encoding The encoding type to use, 7bit, base64,
299
+ * or quoted-printable.
300
+ * @access private
301
+ */
302
+ function _getEncodedData($data, $encoding)
303
+ {
304
+ switch ($encoding) {
305
+ case '8bit':
306
+ case '7bit':
307
+ return $data;
308
+ break;
309
+
310
+ case 'quoted-printable':
311
+ return $this->_quotedPrintableEncode($data);
312
+ break;
313
+
314
+ case 'base64':
315
+ return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF));
316
+ break;
317
+
318
+ default:
319
+ return $data;
320
+ }
321
+ }
322
+
323
+ /**
324
+ * quotedPrintableEncode()
325
+ *
326
+ * Encodes data to quoted-printable standard.
327
+ *
328
+ * @param $input The data to encode
329
+ * @param $line_max Optional max line length. Should
330
+ * not be more than 76 chars
331
+ *
332
+ * @access private
333
+ */
334
+ function _quotedPrintableEncode($input , $line_max = 76)
335
+ {
336
+ $lines = preg_split("/\r?\n/", $input);
337
+ $eol = MAIL_MIMEPART_CRLF;
338
+ $escape = '=';
339
+ $output = '';
340
+
341
+ while (list(, $line) = each($lines)) {
342
+
343
+ $line = preg_split('||', $line, -1, PREG_SPLIT_NO_EMPTY);
344
+ $linlen = count($line);
345
+ $newline = '';
346
+
347
+ for ($i = 0; $i < $linlen; $i++) {
348
+ $char = $line[$i];
349
+ $dec = ord($char);
350
+
351
+ if (($dec == 32) AND ($i == ($linlen - 1))) { // convert space at eol only
352
+ $char = '=20';
353
+
354
+ } elseif (($dec == 9) AND ($i == ($linlen - 1))) { // convert tab at eol only
355
+ $char = '=09';
356
+ } elseif ($dec == 9) {
357
+ ; // Do nothing if a tab.
358
+ } elseif (($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) {
359
+ $char = $escape . strtoupper(sprintf('%02s', dechex($dec)));
360
+ } elseif (($dec == 46) AND ($newline == '')) {
361
+ //Bug #9722: convert full-stop at bol
362
+ //Some Windows servers need this, won't break anything (cipri)
363
+ $char = '=2E';
364
+ }
365
+
366
+ if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted
367
+ $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay
368
+ $newline = '';
369
+ }
370
+ $newline .= $char;
371
+ } // end of for
372
+ $output .= $newline . $eol;
373
+ }
374
+ $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf
375
+ return $output;
376
+ }
377
+
378
+ /**
379
+ * _buildHeaderParam()
380
+ *
381
+ * Encodes the paramater of a header.
382
+ *
383
+ * @param $name The name of the header-parameter
384
+ * @param $value The value of the paramter
385
+ * @param $charset The characterset of $value
386
+ * @param $language The language used in $value
387
+ * @param $maxLength The maximum length of a line. Defauls to 75
388
+ *
389
+ * @access private
390
+ */
391
+ function _buildHeaderParam($name, $value, $charset=NULL, $language=NULL, $maxLength=75)
392
+ {
393
+ //If we find chars to encode, or if charset or language
394
+ //is not any of the defaults, we need to encode the value.
395
+ $shouldEncode = 0;
396
+ $secondAsterisk = '';
397
+ if (preg_match('#([\x80-\xFF]){1}#', $value)) {
398
+ $shouldEncode = 1;
399
+ } elseif ($charset && (strtolower($charset) != 'us-ascii')) {
400
+ $shouldEncode = 1;
401
+ } elseif ($language && ($language != 'en' && $language != 'en-us')) {
402
+ $shouldEncode = 1;
403
+ }
404
+ if ($shouldEncode) {
405
+ $search = array('%', ' ', "\t");
406
+ $replace = array('%25', '%20', '%09');
407
+ $encValue = str_replace($search, $replace, $value);
408
+ $encValue = preg_replace('#([\x80-\xFF])#e', '"%" . strtoupper(dechex(ord("\1")))', $encValue);
409
+ $value = "$charset'$language'$encValue";
410
+ $secondAsterisk = '*';
411
+ }
412
+ $header = " {$name}{$secondAsterisk}=\"{$value}\"; ";
413
+ if (strlen($header) <= $maxLength) {
414
+ return $header;
415
+ }
416
+
417
+ $preLength = strlen(" {$name}*0{$secondAsterisk}=\"");
418
+ $sufLength = strlen("\";");
419
+ $maxLength = MAX(16, $maxLength - $preLength - $sufLength - 2);
420
+ $maxLengthReg = "|(.{0,$maxLength}[^\%][^\%])|";
421
+
422
+ $headers = array();
423
+ $headCount = 0;
424
+ while ($value) {
425
+ $matches = array();
426
+ $found = preg_match($maxLengthReg, $value, $matches);
427
+ if ($found) {
428
+ $headers[] = " {$name}*{$headCount}{$secondAsterisk}=\"{$matches[0]}\"";
429
+ $value = substr($value, strlen($matches[0]));
430
+ } else {
431
+ $headers[] = " {$name}*{$headCount}{$secondAsterisk}=\"{$value}\"";
432
+ $value = "";
433
+ }
434
+ $headCount++;
435
+ }
436
+ $headers = implode(MAIL_MIMEPART_CRLF, $headers) . ';';
437
+ return $headers;
438
+ }
439
+ } // End of class
lib/PEAR/Mail/xmail.dtd ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+
3
+ <!ENTITY lt "&#38;#60;">
4
+ <!ENTITY gt "&#62;">
5
+ <!ENTITY amp "&#38;#38;">
6
+ <!ENTITY apos "&#39;">
7
+ <!ENTITY quot "&#34;">
8
+ <!ENTITY crlf "&#13;&#10;">
9
+
10
+ <!ELEMENT email (header+, (body | mimepart+))>
11
+ <!ELEMENT mimepart (header+, (body | mimepart+))>
12
+ <!ELEMENT body (#PCDATA)>
13
+ <!ELEMENT header ((headername|headervalue|parameter)*)>
14
+ <!ELEMENT headername (#PCDATA)>
15
+ <!ELEMENT headervalue (#PCDATA)>
16
+ <!ELEMENT parameter ((paramname|paramvalue)+)>
17
+ <!ELEMENT paramvalue (#PCDATA)>
18
+ <!ELEMENT paramname (#PCDATA)>
19
+
lib/PEAR/Mail/xmail.xsl ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="ISO-8859-1"?>
2
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
3
+ <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
4
+ <xsl:preserve-space elements="headervalue paramvalue body"/>
5
+
6
+ <xsl:template name="mimepart">
7
+
8
+ <xsl:variable name="boundary">
9
+ <xsl:for-each select="./header">
10
+ <xsl:if test="string(./headername) = 'Content-Type'">
11
+ <xsl:for-each select="./parameter">
12
+ <xsl:if test="string(./paramname) = 'boundary'">
13
+ <xsl:value-of select="paramvalue"/>
14
+ </xsl:if>
15
+ </xsl:for-each>
16
+ </xsl:if>
17
+ </xsl:for-each>
18
+ </xsl:variable>
19
+
20
+ <xsl:for-each select="header">
21
+
22
+ <xsl:value-of select="headername"/>
23
+ <xsl:text>: </xsl:text>
24
+ <xsl:value-of select="headervalue"/>
25
+
26
+ <xsl:if test="count(./parameter) = 0">
27
+ <xsl:text>&#13;&#10;</xsl:text>
28
+ </xsl:if>
29
+
30
+ <xsl:for-each select="parameter">
31
+ <xsl:text>;&#13;&#10;&#09;</xsl:text>
32
+ <xsl:value-of select="paramname"/>
33
+ <xsl:text>="</xsl:text>
34
+ <xsl:value-of select="paramvalue"/>
35
+ <xsl:text>"</xsl:text>
36
+ </xsl:for-each>
37
+
38
+ <xsl:if test="count(./parameter) > 0">
39
+ <xsl:text>&#13;&#10;</xsl:text>
40
+ </xsl:if>
41
+
42
+ </xsl:for-each>
43
+
44
+ <xsl:text>&#13;&#10;</xsl:text>
45
+
46
+ <!-- Which to do, print a body or process subparts? -->
47
+ <xsl:choose>
48
+ <xsl:when test="count(./mimepart) = 0">
49
+ <xsl:value-of select="body"/>
50
+ <xsl:text>&#13;&#10;</xsl:text>
51
+ </xsl:when>
52
+
53
+ <xsl:otherwise>
54
+ <xsl:for-each select="mimepart">
55
+ <xsl:text>--</xsl:text><xsl:value-of select="$boundary"/><xsl:text>&#13;&#10;</xsl:text>
56
+ <xsl:call-template name="mimepart"/>
57
+ </xsl:for-each>
58
+
59
+ <xsl:text>--</xsl:text><xsl:value-of select="$boundary"/><xsl:text>--&#13;&#10;</xsl:text>
60
+
61
+ </xsl:otherwise>
62
+ </xsl:choose>
63
+ </xsl:template>
64
+
65
+ <!-- This is where the stylesheet really starts, matching the top level email element -->
66
+ <xsl:template match="email">
67
+ <xsl:call-template name="mimepart"/>
68
+ </xsl:template>
69
+
70
+ </xsl:stylesheet>
lib/PEAR/Net/Socket.php ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // +----------------------------------------------------------------------+
4
+ // | PHP Version 4 |
5
+ // +----------------------------------------------------------------------+
6
+ // | Copyright (c) 1997-2003 The PHP Group |
7
+ // +----------------------------------------------------------------------+
8
+ // | This source file is subject to version 2.0 of the PHP license, |
9
+ // | that is bundled with this package in the file LICENSE, and is |
10
+ // | available at through the world-wide-web at |
11
+ // | http://www.php.net/license/2_02.txt. |
12
+ // | If you did not receive a copy of the PHP license and are unable to |
13
+ // | obtain it through the world-wide-web, please send a note to |
14
+ // | license@php.net so we can mail you a copy immediately. |
15
+ // +----------------------------------------------------------------------+
16
+ // | Authors: Stig Bakken <ssb@php.net> |
17
+ // | Chuck Hagenbuch <chuck@horde.org> |
18
+ // +----------------------------------------------------------------------+
19
+ //
20
+ // $Id: Socket.php,v 1.38 2008/02/15 18:24:17 chagenbu Exp $
21
+
22
+ require_once 'PEAR.php';
23
+
24
+ define('NET_SOCKET_READ', 1);
25
+ define('NET_SOCKET_WRITE', 2);
26
+ define('NET_SOCKET_ERROR', 4);
27
+
28
+ /**
29
+ * Generalized Socket class.
30
+ *
31
+ * @version 1.1
32
+ * @author Stig Bakken <ssb@php.net>
33
+ * @author Chuck Hagenbuch <chuck@horde.org>
34
+ */
35
+ class Net_Socket extends PEAR {
36
+
37
+ /**
38
+ * Socket file pointer.
39
+ * @var resource $fp
40
+ */
41
+ var $fp = null;
42
+
43
+ /**
44
+ * Whether the socket is blocking. Defaults to true.
45
+ * @var boolean $blocking
46
+ */
47
+ var $blocking = true;
48
+
49
+ /**
50
+ * Whether the socket is persistent. Defaults to false.
51
+ * @var boolean $persistent
52
+ */
53
+ var $persistent = false;
54
+
55
+ /**
56
+ * The IP address to connect to.
57
+ * @var string $addr
58
+ */
59
+ var $addr = '';
60
+
61
+ /**
62
+ * The port number to connect to.
63
+ * @var integer $port
64
+ */
65
+ var $port = 0;
66
+
67
+ /**
68
+ * Number of seconds to wait on socket connections before assuming
69
+ * there's no more data. Defaults to no timeout.
70
+ * @var integer $timeout
71
+ */
72
+ var $timeout = false;
73
+
74
+ /**
75
+ * Number of bytes to read at a time in readLine() and
76
+ * readAll(). Defaults to 2048.
77
+ * @var integer $lineLength
78
+ */
79
+ var $lineLength = 2048;
80
+
81
+ /**
82
+ * Connect to the specified port. If called when the socket is
83
+ * already connected, it disconnects and connects again.
84
+ *
85
+ * @param string $addr IP address or host name.
86
+ * @param integer $port TCP port number.
87
+ * @param boolean $persistent (optional) Whether the connection is
88
+ * persistent (kept open between requests
89
+ * by the web server).
90
+ * @param integer $timeout (optional) How long to wait for data.
91
+ * @param array $options See options for stream_context_create.
92
+ *
93
+ * @access public
94
+ *
95
+ * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
96
+ */
97
+ function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
98
+ {
99
+ if (is_resource($this->fp)) {
100
+ @fclose($this->fp);
101
+ $this->fp = null;
102
+ }
103
+
104
+ if (!$addr) {
105
+ return $this->raiseError('$addr cannot be empty');
106
+ } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
107
+ strstr($addr, '/') !== false) {
108
+ $this->addr = $addr;
109
+ } else {
110
+ $this->addr = @gethostbyname($addr);
111
+ }
112
+
113
+ $this->port = $port % 65536;
114
+
115
+ if ($persistent !== null) {
116
+ $this->persistent = $persistent;
117
+ }
118
+
119
+ if ($timeout !== null) {
120
+ $this->timeout = $timeout;
121
+ }
122
+
123
+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
124
+ $errno = 0;
125
+ $errstr = '';
126
+ $old_track_errors = @ini_set('track_errors', 1);
127
+ if ($options && function_exists('stream_context_create')) {
128
+ if ($this->timeout) {
129
+ $timeout = $this->timeout;
130
+ } else {
131
+ $timeout = 0;
132
+ }
133
+ $context = stream_context_create($options);
134
+
135
+ // Since PHP 5 fsockopen doesn't allow context specification
136
+ if (function_exists('stream_socket_client')) {
137
+ $flags = $this->persistent ? STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT;
138
+ $addr = $this->addr . ':' . $this->port;
139
+ $fp = stream_socket_client($addr, $errno, $errstr, $timeout, $flags, $context);
140
+ } else {
141
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
142
+ }
143
+ } else {
144
+ if ($this->timeout) {
145
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
146
+ } else {
147
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
148
+ }
149
+ }
150
+
151
+ if (!$fp) {
152
+ if ($errno == 0 && isset($php_errormsg)) {
153
+ $errstr = $php_errormsg;
154
+ }
155
+ @ini_set('track_errors', $old_track_errors);
156
+ return $this->raiseError($errstr, $errno);
157
+ }
158
+
159
+ @ini_set('track_errors', $old_track_errors);
160
+ $this->fp = $fp;
161
+
162
+ return $this->setBlocking($this->blocking);
163
+ }
164
+
165
+ /**
166
+ * Disconnects from the peer, closes the socket.
167
+ *
168
+ * @access public
169
+ * @return mixed true on success or a PEAR_Error instance otherwise
170
+ */
171
+ function disconnect()
172
+ {
173
+ if (!is_resource($this->fp)) {
174
+ return $this->raiseError('not connected');
175
+ }
176
+
177
+ @fclose($this->fp);
178
+ $this->fp = null;
179
+ return true;
180
+ }
181
+
182
+ /**
183
+ * Find out if the socket is in blocking mode.
184
+ *
185
+ * @access public
186
+ * @return boolean The current blocking mode.
187
+ */
188
+ function isBlocking()
189
+ {
190
+ return $this->blocking;
191
+ }
192
+
193
+ /**
194
+ * Sets whether the socket connection should be blocking or
195
+ * not. A read call to a non-blocking socket will return immediately
196
+ * if there is no data available, whereas it will block until there
197
+ * is data for blocking sockets.
198
+ *
199
+ * @param boolean $mode True for blocking sockets, false for nonblocking.
200
+ * @access public
201
+ * @return mixed true on success or a PEAR_Error instance otherwise
202
+ */
203
+ function setBlocking($mode)
204
+ {
205
+ if (!is_resource($this->fp)) {
206
+ return $this->raiseError('not connected');
207
+ }
208
+
209
+ $this->blocking = $mode;
210
+ socket_set_blocking($this->fp, $this->blocking);
211
+ return true;
212
+ }
213
+
214
+ /**
215
+ * Sets the timeout value on socket descriptor,
216
+ * expressed in the sum of seconds and microseconds
217
+ *
218
+ * @param integer $seconds Seconds.
219
+ * @param integer $microseconds Microseconds.
220
+ * @access public
221
+ * @return mixed true on success or a PEAR_Error instance otherwise
222
+ */
223
+ function setTimeout($seconds, $microseconds)
224
+ {
225
+ if (!is_resource($this->fp)) {
226
+ return $this->raiseError('not connected');
227
+ }
228
+
229
+ return socket_set_timeout($this->fp, $seconds, $microseconds);
230
+ }
231
+
232
+ /**
233
+ * Sets the file buffering size on the stream.
234
+ * See php's stream_set_write_buffer for more information.
235
+ *
236
+ * @param integer $size Write buffer size.
237
+ * @access public
238
+ * @return mixed on success or an PEAR_Error object otherwise
239
+ */
240
+ function setWriteBuffer($size)
241
+ {
242
+ if (!is_resource($this->fp)) {
243
+ return $this->raiseError('not connected');
244
+ }
245
+
246
+ $returned = stream_set_write_buffer($this->fp, $size);
247
+ if ($returned == 0) {
248
+ return true;
249
+ }
250
+ return $this->raiseError('Cannot set write buffer.');
251
+ }
252
+
253
+ /**
254
+ * Returns information about an existing socket resource.
255
+ * Currently returns four entries in the result array:
256
+ *
257
+ * <p>
258
+ * timed_out (bool) - The socket timed out waiting for data<br>
259
+ * blocked (bool) - The socket was blocked<br>
260
+ * eof (bool) - Indicates EOF event<br>
261
+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
262
+ * </p>
263
+ *
264
+ * @access public
265
+ * @return mixed Array containing information about existing socket resource or a PEAR_Error instance otherwise
266
+ */
267
+ function getStatus()
268
+ {
269
+ if (!is_resource($this->fp)) {
270
+ return $this->raiseError('not connected');
271
+ }
272
+
273
+ return socket_get_status($this->fp);
274
+ }
275
+
276
+ /**
277
+ * Get a specified line of data
278
+ *
279
+ * @access public
280
+ * @return $size bytes of data from the socket, or a PEAR_Error if
281
+ * not connected.
282
+ */
283
+ function gets($size)
284
+ {
285
+ if (!is_resource($this->fp)) {
286
+ return $this->raiseError('not connected');
287
+ }
288
+
289
+ return @fgets($this->fp, $size);
290
+ }
291
+
292
+ /**
293
+ * Read a specified amount of data. This is guaranteed to return,
294
+ * and has the added benefit of getting everything in one fread()
295
+ * chunk; if you know the size of the data you're getting
296
+ * beforehand, this is definitely the way to go.
297
+ *
298
+ * @param integer $size The number of bytes to read from the socket.
299
+ * @access public
300
+ * @return $size bytes of data from the socket, or a PEAR_Error if
301
+ * not connected.
302
+ */
303
+ function read($size)
304
+ {
305
+ if (!is_resource($this->fp)) {
306
+ return $this->raiseError('not connected');
307
+ }
308
+
309
+ return @fread($this->fp, $size);
310
+ }
311
+
312
+ /**
313
+ * Write a specified amount of data.
314
+ *
315
+ * @param string $data Data to write.
316
+ * @param integer $blocksize Amount of data to write at once.
317
+ * NULL means all at once.
318
+ *
319
+ * @access public
320
+ * @return mixed If the socket is not connected, returns an instance of PEAR_Error
321
+ * If the write succeeds, returns the number of bytes written
322
+ * If the write fails, returns false.
323
+ */
324
+ function write($data, $blocksize = null)
325
+ {
326
+ if (!is_resource($this->fp)) {
327
+ return $this->raiseError('not connected');
328
+ }
329
+
330
+ if (is_null($blocksize) && !OS_WINDOWS) {
331
+ return @fwrite($this->fp, $data);
332
+ } else {
333
+ if (is_null($blocksize)) {
334
+ $blocksize = 1024;
335
+ }
336
+
337
+ $pos = 0;
338
+ $size = strlen($data);
339
+ while ($pos < $size) {
340
+ $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
341
+ if ($written === false) {
342
+ return false;
343
+ }
344
+ $pos += $written;
345
+ }
346
+
347
+ return $pos;
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Write a line of data to the socket, followed by a trailing "\r\n".
353
+ *
354
+ * @access public
355
+ * @return mixed fputs result, or an error
356
+ */
357
+ function writeLine($data)
358
+ {
359
+ if (!is_resource($this->fp)) {
360
+ return $this->raiseError('not connected');
361
+ }
362
+
363
+ return fwrite($this->fp, $data . "\r\n");
364
+ }
365
+
366
+ /**
367
+ * Tests for end-of-file on a socket descriptor.
368
+ *
369
+ * Also returns true if the socket is disconnected.
370
+ *
371
+ * @access public
372
+ * @return bool
373
+ */
374
+ function eof()
375
+ {
376
+ return (!is_resource($this->fp) || feof($this->fp));
377
+ }
378
+
379
+ /**
380
+ * Reads a byte of data
381
+ *
382
+ * @access public
383
+ * @return 1 byte of data from the socket, or a PEAR_Error if
384
+ * not connected.
385
+ */
386
+ function readByte()
387
+ {
388
+ if (!is_resource($this->fp)) {
389
+ return $this->raiseError('not connected');
390
+ }
391
+
392
+ return ord(@fread($this->fp, 1));
393
+ }
394
+
395
+ /**
396
+ * Reads a word of data
397
+ *
398
+ * @access public
399
+ * @return 1 word of data from the socket, or a PEAR_Error if
400
+ * not connected.
401
+ */
402
+ function readWord()
403
+ {
404
+ if (!is_resource($this->fp)) {
405
+ return $this->raiseError('not connected');
406
+ }
407
+
408
+ $buf = @fread($this->fp, 2);
409
+ return (ord($buf[0]) + (ord($buf[1]) << 8));
410
+ }
411
+
412
+ /**
413
+ * Reads an int of data
414
+ *
415
+ * @access public
416
+ * @return integer 1 int of data from the socket, or a PEAR_Error if
417
+ * not connected.
418
+ */
419
+ function readInt()
420
+ {
421
+ if (!is_resource($this->fp)) {
422
+ return $this->raiseError('not connected');
423
+ }
424
+
425
+ $buf = @fread($this->fp, 4);
426
+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
427
+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
428
+ }
429
+
430
+ /**
431
+ * Reads a zero-terminated string of data
432
+ *
433
+ * @access public
434
+ * @return string, or a PEAR_Error if
435
+ * not connected.
436
+ */
437
+ function readString()
438
+ {
439
+ if (!is_resource($this->fp)) {
440
+ return $this->raiseError('not connected');
441
+ }
442
+
443
+ $string = '';
444
+ while (($char = @fread($this->fp, 1)) != "\x00") {
445
+ $string .= $char;
446
+ }
447
+ return $string;
448
+ }
449
+
450
+ /**
451
+ * Reads an IP Address and returns it in a dot formatted string
452
+ *
453
+ * @access public
454
+ * @return Dot formatted string, or a PEAR_Error if
455
+ * not connected.
456
+ */
457
+ function readIPAddress()
458
+ {
459
+ if (!is_resource($this->fp)) {
460
+ return $this->raiseError('not connected');
461
+ }
462
+
463
+ $buf = @fread($this->fp, 4);
464
+ return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
465
+ ord($buf[2]), ord($buf[3]));
466
+ }
467
+
468
+ /**
469
+ * Read until either the end of the socket or a newline, whichever
470
+ * comes first. Strips the trailing newline from the returned data.
471
+ *
472
+ * @access public
473
+ * @return All available data up to a newline, without that
474
+ * newline, or until the end of the socket, or a PEAR_Error if
475
+ * not connected.
476
+ */
477
+ function readLine()
478
+ {
479
+ if (!is_resource($this->fp)) {
480
+ return $this->raiseError('not connected');
481
+ }
482
+
483
+ $line = '';
484
+ $timeout = time() + $this->timeout;
485
+ while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
486
+ $line .= @fgets($this->fp, $this->lineLength);
487
+ if (substr($line, -1) == "\n") {
488
+ return rtrim($line, "\r\n");
489
+ }
490
+ }
491
+ return $line;
492
+ }
493
+
494
+ /**
495
+ * Read until the socket closes, or until there is no more data in
496
+ * the inner PHP buffer. If the inner buffer is empty, in blocking
497
+ * mode we wait for at least 1 byte of data. Therefore, in
498
+ * blocking mode, if there is no data at all to be read, this
499
+ * function will never exit (unless the socket is closed on the
500
+ * remote end).
501
+ *
502
+ * @access public
503
+ *
504
+ * @return string All data until the socket closes, or a PEAR_Error if
505
+ * not connected.
506
+ */
507
+ function readAll()
508
+ {
509
+ if (!is_resource($this->fp)) {
510
+ return $this->raiseError('not connected');
511
+ }
512
+
513
+ $data = '';
514
+ while (!feof($this->fp)) {
515
+ $data .= @fread($this->fp, $this->lineLength);
516
+ }
517
+ return $data;
518
+ }
519
+
520
+ /**
521
+ * Runs the equivalent of the select() system call on the socket
522
+ * with a timeout specified by tv_sec and tv_usec.
523
+ *
524
+ * @param integer $state Which of read/write/error to check for.
525
+ * @param integer $tv_sec Number of seconds for timeout.
526
+ * @param integer $tv_usec Number of microseconds for timeout.
527
+ *
528
+ * @access public
529
+ * @return False if select fails, integer describing which of read/write/error
530
+ * are ready, or PEAR_Error if not connected.
531
+ */
532
+ function select($state, $tv_sec, $tv_usec = 0)
533
+ {
534
+ if (!is_resource($this->fp)) {
535
+ return $this->raiseError('not connected');
536
+ }
537
+
538
+ $read = null;
539
+ $write = null;
540
+ $except = null;
541
+ if ($state & NET_SOCKET_READ) {
542
+ $read[] = $this->fp;
543
+ }
544
+ if ($state & NET_SOCKET_WRITE) {
545
+ $write[] = $this->fp;
546
+ }
547
+ if ($state & NET_SOCKET_ERROR) {
548
+ $except[] = $this->fp;
549
+ }
550
+ if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
551
+ return false;
552
+ }
553
+
554
+ $result = 0;
555
+ if (count($read)) {
556
+ $result |= NET_SOCKET_READ;
557
+ }
558
+ if (count($write)) {
559
+ $result |= NET_SOCKET_WRITE;
560
+ }
561
+ if (count($except)) {
562
+ $result |= NET_SOCKET_ERROR;
563
+ }
564
+ return $result;
565
+ }
566
+
567
+ /**
568
+ * Turns encryption on/off on a connected socket.
569
+ *
570
+ * @param bool $enabled Set this parameter to true to enable encryption
571
+ * and false to disable encryption.
572
+ * @param integer $type Type of encryption. See
573
+ * http://se.php.net/manual/en/function.stream-socket-enable-crypto.php for values.
574
+ *
575
+ * @access public
576
+ * @return false on error, true on success and 0 if there isn't enough data and the
577
+ * user should try again (non-blocking sockets only). A PEAR_Error object
578
+ * is returned if the socket is not connected
579
+ */
580
+ function enableCrypto($enabled, $type)
581
+ {
582
+ if (version_compare(phpversion(), "5.1.0", ">=")) {
583
+ if (!is_resource($this->fp)) {
584
+ return $this->raiseError('not connected');
585
+ }
586
+ return @stream_socket_enable_crypto($this->fp, $enabled, $type);
587
+ } else {
588
+ return $this->raiseError('Net_Socket::enableCrypto() requires php version >= 5.1.0');
589
+ }
590
+ }
591
+
592
+ }
lib/PEAR/Net/URL.php ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // +-----------------------------------------------------------------------+
3
+ // | Copyright (c) 2002-2004, Richard Heyes |
4
+ // | All rights reserved. |
5
+ // | |
6
+ // | Redistribution and use in source and binary forms, with or without |
7
+ // | modification, are permitted provided that the following conditions |
8
+ // | are met: |
9
+ // | |
10
+ // | o Redistributions of source code must retain the above copyright |
11
+ // | notice, this list of conditions and the following disclaimer. |
12
+ // | o Redistributions in binary form must reproduce the above copyright |
13
+ // | notice, this list of conditions and the following disclaimer in the |
14
+ // | documentation and/or other materials provided with the distribution.|
15
+ // | o The names of the authors may not be used to endorse or promote |
16
+ // | products derived from this software without specific prior written |
17
+ // | permission. |
18
+ // | |
19
+ // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20
+ // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21
+ // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22
+ // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23
+ // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24
+ // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25
+ // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26
+ // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27
+ // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28
+ // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29
+ // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30
+ // | |
31
+ // +-----------------------------------------------------------------------+
32
+ // | Author: Richard Heyes <richard at php net> |
33
+ // +-----------------------------------------------------------------------+
34
+ //
35
+ // $Id: URL.php,v 1.49 2007/06/28 14:43:07 davidc Exp $
36
+ //
37
+ // Net_URL Class
38
+
39
+
40
+ class Net_URL
41
+ {
42
+ var $options = array('encode_query_keys' => false);
43
+ /**
44
+ * Full url
45
+ * @var string
46
+ */
47
+ var $url;
48
+
49
+ /**
50
+ * Protocol
51
+ * @var string
52
+ */
53
+ var $protocol;
54
+
55
+ /**
56
+ * Username
57
+ * @var string
58
+ */
59
+ var $username;
60
+
61
+ /**
62
+ * Password
63
+ * @var string
64
+ */
65
+ var $password;
66
+
67
+ /**
68
+ * Host
69
+ * @var string
70
+ */
71
+ var $host;
72
+
73
+ /**
74
+ * Port
75
+ * @var integer
76
+ */
77
+ var $port;
78
+
79
+ /**
80
+ * Path
81
+ * @var string
82
+ */
83
+ var $path;
84
+
85
+ /**
86
+ * Query string
87
+ * @var array
88
+ */
89
+ var $querystring;
90
+
91
+ /**
92
+ * Anchor
93
+ * @var string
94
+ */
95
+ var $anchor;
96
+
97
+ /**
98
+ * Whether to use []
99
+ * @var bool
100
+ */
101
+ var $useBrackets;
102
+
103
+ /**
104
+ * PHP4 Constructor
105
+ *
106
+ * @see __construct()
107
+ */
108
+ // function Net_URL($url = null, $useBrackets = true)
109
+ // {
110
+ // $this->__construct($url, $useBrackets);
111
+ // }
112
+
113
+ /**
114
+ * PHP5 Constructor
115
+ *
116
+ * Parses the given url and stores the various parts
117
+ * Defaults are used in certain cases
118
+ *
119
+ * @param string $url Optional URL
120
+ * @param bool $useBrackets Whether to use square brackets when
121
+ * multiple querystrings with the same name
122
+ * exist
123
+ */
124
+ function __construct($url = null, $useBrackets = true)
125
+ {
126
+ $this->url = $url;
127
+ $this->useBrackets = $useBrackets;
128
+
129
+ $this->initialize();
130
+ }
131
+
132
+ function initialize()
133
+ {
134
+ $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
135
+
136
+ $this->user = '';
137
+ $this->pass = '';
138
+ $this->host = '';
139
+ $this->port = 80;
140
+ $this->path = '';
141
+ $this->querystring = array();
142
+ $this->anchor = '';
143
+
144
+ // Only use defaults if not an absolute URL given
145
+ if (!preg_match('/^[a-z0-9]+:\/\//i', $this->url)) {
146
+ $this->protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http');
147
+
148
+ /**
149
+ * Figure out host/port
150
+ */
151
+ if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) &&
152
+ preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches))
153
+ {
154
+ $host = $matches[1];
155
+ if (!empty($matches[3])) {
156
+ $port = $matches[3];
157
+ } else {
158
+ $port = $this->getStandardPort($this->protocol);
159
+ }
160
+ }
161
+
162
+ $this->user = '';
163
+ $this->pass = '';
164
+ $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
165
+ $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
166
+ $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
167
+ $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
168
+ $this->anchor = '';
169
+ }
170
+
171
+ // Parse the url and store the various parts
172
+ if (!empty($this->url)) {
173
+ $urlinfo = parse_url($this->url);
174
+
175
+ // Default querystring
176
+ $this->querystring = array();
177
+
178
+ foreach ($urlinfo as $key => $value) {
179
+ switch ($key) {
180
+ case 'scheme':
181
+ $this->protocol = $value;
182
+ $this->port = $this->getStandardPort($value);
183
+ break;
184
+
185
+ case 'user':
186
+ case 'pass':
187
+ case 'host':
188
+ case 'port':
189
+ $this->$key = $value;
190
+ break;
191
+
192
+ case 'path':
193
+ if ($value{0} == '/') {
194
+ $this->path = $value;
195
+ } else {
196
+ $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
197
+ $this->path = sprintf('%s/%s', $path, $value);
198
+ }
199
+ break;
200
+
201
+ case 'query':
202
+ $this->querystring = $this->_parseRawQueryString($value);
203
+ break;
204
+
205
+ case 'fragment':
206
+ $this->anchor = $value;
207
+ break;
208
+ }
209
+ }
210
+ }
211
+ }
212
+ /**
213
+ * Returns full url
214
+ *
215
+ * @return string Full url
216
+ * @access public
217
+ */
218
+ function getURL()
219
+ {
220
+ $querystring = $this->getQueryString();
221
+
222
+ $this->url = $this->protocol . '://'
223
+ . $this->user . (!empty($this->pass) ? ':' : '')
224
+ . $this->pass . (!empty($this->user) ? '@' : '')
225
+ . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
226
+ . $this->path
227
+ . (!empty($querystring) ? '?' . $querystring : '')
228
+ . (!empty($this->anchor) ? '#' . $this->anchor : '');
229
+
230
+ return $this->url;
231
+ }
232
+
233
+ /**
234
+ * Adds or updates a querystring item (URL parameter).
235
+ * Automatically encodes parameters with rawurlencode() if $preencoded
236
+ * is false.
237
+ * You can pass an array to $value, it gets mapped via [] in the URL if
238
+ * $this->useBrackets is activated.
239
+ *
240
+ * @param string $name Name of item
241
+ * @param string $value Value of item
242
+ * @param bool $preencoded Whether value is urlencoded or not, default = not
243
+ * @access public
244
+ */
245
+ function addQueryString($name, $value, $preencoded = false)
246
+ {
247
+ if ($this->getOption('encode_query_keys')) {
248
+ $name = rawurlencode($name);
249
+ }
250
+
251
+ if ($preencoded) {
252
+ $this->querystring[$name] = $value;
253
+ } else {
254
+ $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Removes a querystring item
260
+ *
261
+ * @param string $name Name of item
262
+ * @access public
263
+ */
264
+ function removeQueryString($name)
265
+ {
266
+ if ($this->getOption('encode_query_keys')) {
267
+ $name = rawurlencode($name);
268
+ }
269
+
270
+ if (isset($this->querystring[$name])) {
271
+ unset($this->querystring[$name]);
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Sets the querystring to literally what you supply
277
+ *
278
+ * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
279
+ * @access public
280
+ */
281
+ function addRawQueryString($querystring)
282
+ {
283
+ $this->querystring = $this->_parseRawQueryString($querystring);
284
+ }
285
+
286
+ /**
287
+ * Returns flat querystring
288
+ *
289
+ * @return string Querystring
290
+ * @access public
291
+ */
292
+ function getQueryString()
293
+ {
294
+ if (!empty($this->querystring)) {
295
+ foreach ($this->querystring as $name => $value) {
296
+ // Encode var name
297
+ $name = rawurlencode($name);
298
+
299
+ if (is_array($value)) {
300
+ foreach ($value as $k => $v) {
301
+ $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
302
+ }
303
+ } elseif (!is_null($value)) {
304
+ $querystring[] = $name . '=' . $value;
305
+ } else {
306
+ $querystring[] = $name;
307
+ }
308
+ }
309
+ $querystring = implode(ini_get('arg_separator.output'), $querystring);
310
+ } else {
311
+ $querystring = '';
312
+ }
313
+
314
+ return $querystring;
315
+ }
316
+
317
+ /**
318
+ * Parses raw querystring and returns an array of it
319
+ *
320
+ * @param string $querystring The querystring to parse
321
+ * @return array An array of the querystring data
322
+ * @access private
323
+ */
324
+ function _parseRawQuerystring($querystring)
325
+ {
326
+ $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
327
+ $return = array();
328
+
329
+ foreach ($parts as $part) {
330
+ if (strpos($part, '=') !== false) {
331
+ $value = substr($part, strpos($part, '=') + 1);
332
+ $key = substr($part, 0, strpos($part, '='));
333
+ } else {
334
+ $value = null;
335
+ $key = $part;
336
+ }
337
+
338
+ if (!$this->getOption('encode_query_keys')) {
339
+ $key = rawurldecode($key);
340
+ }
341
+
342
+ if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
343
+ $key = $matches[1];
344
+ $idx = $matches[2];
345
+
346
+ // Ensure is an array
347
+ if (empty($return[$key]) || !is_array($return[$key])) {
348
+ $return[$key] = array();
349
+ }
350
+
351
+ // Add data
352
+ if ($idx === '') {
353
+ $return[$key][] = $value;
354
+ } else {
355
+ $return[$key][$idx] = $value;
356
+ }
357
+ } elseif (!$this->useBrackets AND !empty($return[$key])) {
358
+ $return[$key] = (array)$return[$key];
359
+ $return[$key][] = $value;
360
+ } else {
361
+ $return[$key] = $value;
362
+ }
363
+ }
364
+
365
+ return $return;
366
+ }
367
+
368
+ /**
369
+ * Resolves //, ../ and ./ from a path and returns
370
+ * the result. Eg:
371
+ *
372
+ * /foo/bar/../boo.php => /foo/boo.php
373
+ * /foo/bar/../../boo.php => /boo.php
374
+ * /foo/bar/.././/boo.php => /foo/boo.php
375
+ *
376
+ * This method can also be called statically.
377
+ *
378
+ * @param string $path URL path to resolve
379
+ * @return string The result
380
+ */
381
+ function resolvePath($path)
382
+ {
383
+ $path = explode('/', str_replace('//', '/', $path));
384
+
385
+ for ($i=0; $i<count($path); $i++) {
386
+ if ($path[$i] == '.') {
387
+ unset($path[$i]);
388
+ $path = array_values($path);
389
+ $i--;
390
+
391
+ } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
392
+ unset($path[$i]);
393
+ unset($path[$i-1]);
394
+ $path = array_values($path);
395
+ $i -= 2;
396
+
397
+ } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
398
+ unset($path[$i]);
399
+ $path = array_values($path);
400
+ $i--;
401
+
402
+ } else {
403
+ continue;
404
+ }
405
+ }
406
+
407
+ return implode('/', $path);
408
+ }
409
+
410
+ /**
411
+ * Returns the standard port number for a protocol
412
+ *
413
+ * @param string $scheme The protocol to lookup
414
+ * @return integer Port number or NULL if no scheme matches
415
+ *
416
+ * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
417
+ */
418
+ function getStandardPort($scheme)
419
+ {
420
+ switch (strtolower($scheme)) {
421
+ case 'http': return 80;
422
+ case 'https': return 443;
423
+ case 'ftp': return 21;
424
+ case 'imap': return 143;
425
+ case 'imaps': return 993;
426
+ case 'pop3': return 110;
427
+ case 'pop3s': return 995;
428
+ default: return null;
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Forces the URL to a particular protocol
434
+ *
435
+ * @param string $protocol Protocol to force the URL to
436
+ * @param integer $port Optional port (standard port is used by default)
437
+ */
438
+ function setProtocol($protocol, $port = null)
439
+ {
440
+ $this->protocol = $protocol;
441
+ $this->port = is_null($port) ? $this->getStandardPort($protocol) : $port;
442
+ }
443
+
444
+ /**
445
+ * Set an option
446
+ *
447
+ * This function set an option
448
+ * to be used thorough the script.
449
+ *
450
+ * @access public
451
+ * @param string $optionName The optionname to set
452
+ * @param string $value The value of this option.
453
+ */
454
+ function setOption($optionName, $value)
455
+ {
456
+ if (!array_key_exists($optionName, $this->options)) {
457
+ return false;
458
+ }
459
+
460
+ $this->options[$optionName] = $value;
461
+ $this->initialize();
462
+ }
463
+
464
+ /**
465
+ * Get an option
466
+ *
467
+ * This function gets an option
468
+ * from the $this->options array
469
+ * and return it's value.
470
+ *
471
+ * @access public
472
+ * @param string $opionName The name of the option to retrieve
473
+ * @see $this->options
474
+ */
475
+ function getOption($optionName)
476
+ {
477
+ if (!isset($this->options[$optionName])) {
478
+ return false;
479
+ }
480
+
481
+ return $this->options[$optionName];
482
+ }
483
+
484
+ }
485
+ ?>
lib/PEAR/PEAR.php ADDED
@@ -0,0 +1,1118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PEAR, the PHP Extension and Application Repository
4
+ *
5
+ * PEAR class and PEAR_Error class
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
10
+ * that is available through the world-wide-web at the following URI:
11
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
12
+ * the PHP License and are unable to obtain it through the web, please
13
+ * send a note to license@php.net so we can mail you a copy immediately.
14
+ *
15
+ * @category pear
16
+ * @package PEAR
17
+ * @author Sterling Hughes <sterling@php.net>
18
+ * @author Stig Bakken <ssb@php.net>
19
+ * @author Tomas V.V.Cox <cox@idecnet.com>
20
+ * @author Greg Beaver <cellog@php.net>
21
+ * @copyright 1997-2008 The PHP Group
22
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
23
+ * @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
24
+ * @link http://pear.php.net/package/PEAR
25
+ * @since File available since Release 0.1
26
+ */
27
+
28
+ /**#@+
29
+ * ERROR constants
30
+ */
31
+ define('PEAR_ERROR_RETURN', 1);
32
+ define('PEAR_ERROR_PRINT', 2);
33
+ define('PEAR_ERROR_TRIGGER', 4);
34
+ define('PEAR_ERROR_DIE', 8);
35
+ define('PEAR_ERROR_CALLBACK', 16);
36
+ /**
37
+ * WARNING: obsolete
38
+ * @deprecated
39
+ */
40
+ define('PEAR_ERROR_EXCEPTION', 32);
41
+ /**#@-*/
42
+ define('PEAR_ZE2', (function_exists('version_compare') &&
43
+ version_compare(zend_version(), "2-dev", "ge")));
44
+
45
+ if (substr(PHP_OS, 0, 3) == 'WIN') {
46
+ define('OS_WINDOWS', true);
47
+ define('OS_UNIX', false);
48
+ define('PEAR_OS', 'Windows');
49
+ } else {
50
+ define('OS_WINDOWS', false);
51
+ define('OS_UNIX', true);
52
+ define('PEAR_OS', 'Unix'); // blatant assumption
53
+ }
54
+
55
+ // instant backwards compatibility
56
+ if (!defined('PATH_SEPARATOR')) {
57
+ if (OS_WINDOWS) {
58
+ define('PATH_SEPARATOR', ';');
59
+ } else {
60
+ define('PATH_SEPARATOR', ':');
61
+ }
62
+ }
63
+
64
+ $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
65
+ $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
66
+ $GLOBALS['_PEAR_destructor_object_list'] = array();
67
+ $GLOBALS['_PEAR_shutdown_funcs'] = array();
68
+ $GLOBALS['_PEAR_error_handler_stack'] = array();
69
+
70
+ @ini_set('track_errors', true);
71
+
72
+ /**
73
+ * Base class for other PEAR classes. Provides rudimentary
74
+ * emulation of destructors.
75
+ *
76
+ * If you want a destructor in your class, inherit PEAR and make a
77
+ * destructor method called _yourclassname (same name as the
78
+ * constructor, but with a "_" prefix). Also, in your constructor you
79
+ * have to call the PEAR constructor: $this->PEAR();.
80
+ * The destructor method will be called without parameters. Note that
81
+ * at in some SAPI implementations (such as Apache), any output during
82
+ * the request shutdown (in which destructors are called) seems to be
83
+ * discarded. If you need to get any debug information from your
84
+ * destructor, use error_log(), syslog() or something similar.
85
+ *
86
+ * IMPORTANT! To use the emulated destructors you need to create the
87
+ * objects by reference: $obj =& new PEAR_child;
88
+ *
89
+ * @category pear
90
+ * @package PEAR
91
+ * @author Stig Bakken <ssb@php.net>
92
+ * @author Tomas V.V. Cox <cox@idecnet.com>
93
+ * @author Greg Beaver <cellog@php.net>
94
+ * @copyright 1997-2006 The PHP Group
95
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
96
+ * @version Release: 1.7.2
97
+ * @link http://pear.php.net/package/PEAR
98
+ * @see PEAR_Error
99
+ * @since Class available since PHP 4.0.2
100
+ * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
101
+ */
102
+ class PEAR
103
+ {
104
+ // {{{ properties
105
+
106
+ /**
107
+ * Whether to enable internal debug messages.
108
+ *
109
+ * @var bool
110
+ * @access private
111
+ */
112
+ var $_debug = false;
113
+
114
+ /**
115
+ * Default error mode for this object.
116
+ *
117
+ * @var int
118
+ * @access private
119
+ */
120
+ var $_default_error_mode = null;
121
+
122
+ /**
123
+ * Default error options used for this object when error mode
124
+ * is PEAR_ERROR_TRIGGER.
125
+ *
126
+ * @var int
127
+ * @access private
128
+ */
129
+ var $_default_error_options = null;
130
+
131
+ /**
132
+ * Default error handler (callback) for this object, if error mode is
133
+ * PEAR_ERROR_CALLBACK.
134
+ *
135
+ * @var string
136
+ * @access private
137
+ */
138
+ var $_default_error_handler = '';
139
+
140
+ /**
141
+ * Which class to use for error objects.
142
+ *
143
+ * @var string
144
+ * @access private
145
+ */
146
+ var $_error_class = 'PEAR_Error';
147
+
148
+ /**
149
+ * An array of expected errors.
150
+ *
151
+ * @var array
152
+ * @access private
153
+ */
154
+ var $_expected_errors = array();
155
+
156
+ // }}}
157
+
158
+ // {{{ constructor
159
+
160
+ /**
161
+ * Constructor. Registers this object in
162
+ * $_PEAR_destructor_object_list for destructor emulation if a
163
+ * destructor object exists.
164
+ *
165
+ * @param string $error_class (optional) which class to use for
166
+ * error objects, defaults to PEAR_Error.
167
+ * @access public
168
+ * @return void
169
+ */
170
+ function PEAR($error_class = null)
171
+ {
172
+ $classname = strtolower(get_class($this));
173
+ if ($this->_debug) {
174
+ print "PEAR constructor called, class=$classname\n";
175
+ }
176
+ if ($error_class !== null) {
177
+ $this->_error_class = $error_class;
178
+ }
179
+ while ($classname && strcasecmp($classname, "pear")) {
180
+ $destructor = "_$classname";
181
+ if (method_exists($this, $destructor)) {
182
+ global $_PEAR_destructor_object_list;
183
+ $_PEAR_destructor_object_list[] = &$this;
184
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
185
+ register_shutdown_function("_PEAR_call_destructors");
186
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
187
+ }
188
+ break;
189
+ } else {
190
+ $classname = get_parent_class($classname);
191
+ }
192
+ }
193
+ }
194
+
195
+ // }}}
196
+ // {{{ destructor
197
+
198
+ /**
199
+ * Destructor (the emulated type of...). Does nothing right now,
200
+ * but is included for forward compatibility, so subclass
201
+ * destructors should always call it.
202
+ *
203
+ * See the note in the class desciption about output from
204
+ * destructors.
205
+ *
206
+ * @access public
207
+ * @return void
208
+ */
209
+ function _PEAR() {
210
+ if ($this->_debug) {
211
+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
212
+ }
213
+ }
214
+
215
+ // }}}
216
+ // {{{ getStaticProperty()
217
+
218
+ /**
219
+ * If you have a class that's mostly/entirely static, and you need static
220
+ * properties, you can use this method to simulate them. Eg. in your method(s)
221
+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
222
+ * You MUST use a reference, or they will not persist!
223
+ *
224
+ * @access public
225
+ * @param string $class The calling classname, to prevent clashes
226
+ * @param string $var The variable to retrieve.
227
+ * @return mixed A reference to the variable. If not set it will be
228
+ * auto initialised to NULL.
229
+ */
230
+ public static function &getStaticProperty($class, $var)
231
+ {
232
+ static $properties;
233
+ if (!isset($properties[$class])) {
234
+ $properties[$class] = array();
235
+ }
236
+ if (!array_key_exists($var, $properties[$class])) {
237
+ $properties[$class][$var] = null;
238
+ }
239
+ return $properties[$class][$var];
240
+ }
241
+
242
+ // }}}
243
+ // {{{ registerShutdownFunc()
244
+
245
+ /**
246
+ * Use this function to register a shutdown method for static
247
+ * classes.
248
+ *
249
+ * @access public
250
+ * @param mixed $func The function name (or array of class/method) to call
251
+ * @param mixed $args The arguments to pass to the function
252
+ * @return void
253
+ */
254
+ function registerShutdownFunc($func, $args = array())
255
+ {
256
+ // if we are called statically, there is a potential
257
+ // that no shutdown func is registered. Bug #6445
258
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
259
+ register_shutdown_function("_PEAR_call_destructors");
260
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
261
+ }
262
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
263
+ }
264
+
265
+ // }}}
266
+ // {{{ isError()
267
+
268
+ /**
269
+ * Tell whether a value is a PEAR error.
270
+ *
271
+ * @param mixed $data the value to test
272
+ * @param int $code if $data is an error object, return true
273
+ * only if $code is a string and
274
+ * $obj->getMessage() == $code or
275
+ * $code is an integer and $obj->getCode() == $code
276
+ * @access public
277
+ * @return bool true if parameter is an error
278
+ */
279
+ public static function isError($data, $code = null)
280
+ {
281
+ if ($data instanceof PEAR_Error) {
282
+ if (is_null($code)) {
283
+ return true;
284
+ } elseif (is_string($code)) {
285
+ return $data->getMessage() == $code;
286
+ } else {
287
+ return $data->getCode() == $code;
288
+ }
289
+ }
290
+ return false;
291
+ }
292
+
293
+ // }}}
294
+ // {{{ setErrorHandling()
295
+
296
+ /**
297
+ * Sets how errors generated by this object should be handled.
298
+ * Can be invoked both in objects and statically. If called
299
+ * statically, setErrorHandling sets the default behaviour for all
300
+ * PEAR objects. If called in an object, setErrorHandling sets
301
+ * the default behaviour for that object.
302
+ *
303
+ * @param int $mode
304
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
305
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
306
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
307
+ *
308
+ * @param mixed $options
309
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
310
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
311
+ *
312
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
313
+ * to be the callback function or method. A callback
314
+ * function is a string with the name of the function, a
315
+ * callback method is an array of two elements: the element
316
+ * at index 0 is the object, and the element at index 1 is
317
+ * the name of the method to call in the object.
318
+ *
319
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
320
+ * a printf format string used when printing the error
321
+ * message.
322
+ *
323
+ * @access public
324
+ * @return void
325
+ * @see PEAR_ERROR_RETURN
326
+ * @see PEAR_ERROR_PRINT
327
+ * @see PEAR_ERROR_TRIGGER
328
+ * @see PEAR_ERROR_DIE
329
+ * @see PEAR_ERROR_CALLBACK
330
+ * @see PEAR_ERROR_EXCEPTION
331
+ *
332
+ * @since PHP 4.0.5
333
+ */
334
+
335
+ function setErrorHandling($mode = null, $options = null)
336
+ {
337
+ if (isset($this) && is_a($this, 'PEAR')) {
338
+ $setmode = &$this->_default_error_mode;
339
+ $setoptions = &$this->_default_error_options;
340
+ } else {
341
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
342
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
343
+ }
344
+
345
+ switch ($mode) {
346
+ case PEAR_ERROR_EXCEPTION:
347
+ case PEAR_ERROR_RETURN:
348
+ case PEAR_ERROR_PRINT:
349
+ case PEAR_ERROR_TRIGGER:
350
+ case PEAR_ERROR_DIE:
351
+ case null:
352
+ $setmode = $mode;
353
+ $setoptions = $options;
354
+ break;
355
+
356
+ case PEAR_ERROR_CALLBACK:
357
+ $setmode = $mode;
358
+ // class/object method callback
359
+ if (is_callable($options)) {
360
+ $setoptions = $options;
361
+ } else {
362
+ trigger_error("invalid error callback", E_USER_WARNING);
363
+ }
364
+ break;
365
+
366
+ default:
367
+ trigger_error("invalid error mode", E_USER_WARNING);
368
+ break;
369
+ }
370
+ }
371
+
372
+ // }}}
373
+ // {{{ expectError()
374
+
375
+ /**
376
+ * This method is used to tell which errors you expect to get.
377
+ * Expected errors are always returned with error mode
378
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
379
+ * and this method pushes a new element onto it. The list of
380
+ * expected errors are in effect until they are popped off the
381
+ * stack with the popExpect() method.
382
+ *
383
+ * Note that this method can not be called statically
384
+ *
385
+ * @param mixed $code a single error code or an array of error codes to expect
386
+ *
387
+ * @return int the new depth of the "expected errors" stack
388
+ * @access public
389
+ */
390
+ function expectError($code = '*')
391
+ {
392
+ if (is_array($code)) {
393
+ array_push($this->_expected_errors, $code);
394
+ } else {
395
+ array_push($this->_expected_errors, array($code));
396
+ }
397
+ return sizeof($this->_expected_errors);
398
+ }
399
+
400
+ // }}}
401
+ // {{{ popExpect()
402
+
403
+ /**
404
+ * This method pops one element off the expected error codes
405
+ * stack.
406
+ *
407
+ * @return array the list of error codes that were popped
408
+ */
409
+ function popExpect()
410
+ {
411
+ return array_pop($this->_expected_errors);
412
+ }
413
+
414
+ // }}}
415
+ // {{{ _checkDelExpect()
416
+
417
+ /**
418
+ * This method checks unsets an error code if available
419
+ *
420
+ * @param mixed error code
421
+ * @return bool true if the error code was unset, false otherwise
422
+ * @access private
423
+ * @since PHP 4.3.0
424
+ */
425
+ function _checkDelExpect($error_code)
426
+ {
427
+ $deleted = false;
428
+
429
+ foreach ($this->_expected_errors AS $key => $error_array) {
430
+ if (in_array($error_code, $error_array)) {
431
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
432
+ $deleted = true;
433
+ }
434
+
435
+ // clean up empty arrays
436
+ if (0 == count($this->_expected_errors[$key])) {
437
+ unset($this->_expected_errors[$key]);
438
+ }
439
+ }
440
+ return $deleted;
441
+ }
442
+
443
+ // }}}
444
+ // {{{ delExpect()
445
+
446
+ /**
447
+ * This method deletes all occurences of the specified element from
448
+ * the expected error codes stack.
449
+ *
450
+ * @param mixed $error_code error code that should be deleted
451
+ * @return mixed list of error codes that were deleted or error
452
+ * @access public
453
+ * @since PHP 4.3.0
454
+ */
455
+ function delExpect($error_code)
456
+ {
457
+ $deleted = false;
458
+
459
+ if ((is_array($error_code) && (0 != count($error_code)))) {
460
+ // $error_code is a non-empty array here;
461
+ // we walk through it trying to unset all
462
+ // values
463
+ foreach($error_code as $key => $error) {
464
+ if ($this->_checkDelExpect($error)) {
465
+ $deleted = true;
466
+ } else {
467
+ $deleted = false;
468
+ }
469
+ }
470
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
471
+ } elseif (!empty($error_code)) {
472
+ // $error_code comes alone, trying to unset it
473
+ if ($this->_checkDelExpect($error_code)) {
474
+ return true;
475
+ } else {
476
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
477
+ }
478
+ } else {
479
+ // $error_code is empty
480
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
481
+ }
482
+ }
483
+
484
+ // }}}
485
+ // {{{ raiseError()
486
+
487
+ /**
488
+ * This method is a wrapper that returns an instance of the
489
+ * configured error class with this object's default error
490
+ * handling applied. If the $mode and $options parameters are not
491
+ * specified, the object's defaults are used.
492
+ *
493
+ * @param mixed $message a text error message or a PEAR error object
494
+ *
495
+ * @param int $code a numeric error code (it is up to your class
496
+ * to define these if you want to use codes)
497
+ *
498
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
499
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
500
+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
501
+ *
502
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
503
+ * specifies the PHP-internal error level (one of
504
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
505
+ * If $mode is PEAR_ERROR_CALLBACK, this
506
+ * parameter specifies the callback function or
507
+ * method. In other error modes this parameter
508
+ * is ignored.
509
+ *
510
+ * @param string $userinfo If you need to pass along for example debug
511
+ * information, this parameter is meant for that.
512
+ *
513
+ * @param string $error_class The returned error object will be
514
+ * instantiated from this class, if specified.
515
+ *
516
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
517
+ * the error message parameter will be dropped.
518
+ *
519
+ * @access public
520
+ * @return object a PEAR error object
521
+ * @see PEAR::setErrorHandling
522
+ * @since PHP 4.0.5
523
+ */
524
+ public static function raiseError($message = null,
525
+ $code = null,
526
+ $mode = null,
527
+ $options = null,
528
+ $userinfo = null,
529
+ $error_class = null,
530
+ $skipmsg = false)
531
+ {
532
+ // The error is yet a PEAR error object
533
+ if (is_object($message)) {
534
+ $code = $message->getCode();
535
+ $userinfo = $message->getUserInfo();
536
+ $error_class = $message->getType();
537
+ $message->error_message_prefix = '';
538
+ $message = $message->getMessage();
539
+ }
540
+
541
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
542
+ if ($exp[0] == "*" ||
543
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
544
+ (is_string(reset($exp)) && in_array($message, $exp))) {
545
+ $mode = PEAR_ERROR_RETURN;
546
+ }
547
+ }
548
+ // No mode given, try global ones
549
+ if ($mode === null) {
550
+ // Class error handler
551
+ if (isset($this) && isset($this->_default_error_mode)) {
552
+ $mode = $this->_default_error_mode;
553
+ $options = $this->_default_error_options;
554
+ // Global error handler
555
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
556
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
557
+ $options = $GLOBALS['_PEAR_default_error_options'];
558
+ }
559
+ }
560
+
561
+ if ($error_class !== null) {
562
+ $ec = $error_class;
563
+ } elseif (isset($this) && isset($this->_error_class)) {
564
+ $ec = $this->_error_class;
565
+ } else {
566
+ $ec = 'PEAR_Error';
567
+ }
568
+ if (intval(PHP_VERSION) < 5) {
569
+ // little non-eval hack to fix bug #12147
570
+ include 'PEAR/FixPHP5PEARWarnings.php';
571
+ return $a;
572
+ }
573
+ if ($skipmsg) {
574
+ $a = new $ec($code, $mode, $options, $userinfo);
575
+ } else {
576
+ $a = new $ec($message, $code, $mode, $options, $userinfo);
577
+ }
578
+ return $a;
579
+ }
580
+
581
+ // }}}
582
+ // {{{ throwError()
583
+
584
+ /**
585
+ * Simpler form of raiseError with fewer options. In most cases
586
+ * message, code and userinfo are enough.
587
+ *
588
+ * @param string $message
589
+ *
590
+ */
591
+ function &throwError($message = null,
592
+ $code = null,
593
+ $userinfo = null)
594
+ {
595
+ if (isset($this) && is_a($this, 'PEAR')) {
596
+ $a = &$this->raiseError($message, $code, null, null, $userinfo);
597
+ return $a;
598
+ } else {
599
+ $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
600
+ return $a;
601
+ }
602
+ }
603
+
604
+ // }}}
605
+ function staticPushErrorHandling($mode, $options = null)
606
+ {
607
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
608
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
609
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
610
+ $stack[] = array($def_mode, $def_options);
611
+ switch ($mode) {
612
+ case PEAR_ERROR_EXCEPTION:
613
+ case PEAR_ERROR_RETURN:
614
+ case PEAR_ERROR_PRINT:
615
+ case PEAR_ERROR_TRIGGER:
616
+ case PEAR_ERROR_DIE:
617
+ case null:
618
+ $def_mode = $mode;
619
+ $def_options = $options;
620
+ break;
621
+
622
+ case PEAR_ERROR_CALLBACK:
623
+ $def_mode = $mode;
624
+ // class/object method callback
625
+ if (is_callable($options)) {
626
+ $def_options = $options;
627
+ } else {
628
+ trigger_error("invalid error callback", E_USER_WARNING);
629
+ }
630
+ break;
631
+
632
+ default:
633
+ trigger_error("invalid error mode", E_USER_WARNING);
634
+ break;
635
+ }
636
+ $stack[] = array($mode, $options);
637
+ return true;
638
+ }
639
+
640
+ function staticPopErrorHandling()
641
+ {
642
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
643
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
644
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
645
+ array_pop($stack);
646
+ list($mode, $options) = $stack[sizeof($stack) - 1];
647
+ array_pop($stack);
648
+ switch ($mode) {
649
+ case PEAR_ERROR_EXCEPTION:
650
+ case PEAR_ERROR_RETURN:
651
+ case PEAR_ERROR_PRINT:
652
+ case PEAR_ERROR_TRIGGER:
653
+ case PEAR_ERROR_DIE:
654
+ case null:
655
+ $setmode = $mode;
656
+ $setoptions = $options;
657
+ break;
658
+
659
+ case PEAR_ERROR_CALLBACK:
660
+ $setmode = $mode;
661
+ // class/object method callback
662
+ if (is_callable($options)) {
663
+ $setoptions = $options;
664
+ } else {
665
+ trigger_error("invalid error callback", E_USER_WARNING);
666
+ }
667
+ break;
668
+
669
+ default:
670
+ trigger_error("invalid error mode", E_USER_WARNING);
671
+ break;
672
+ }
673
+ return true;
674
+ }
675
+
676
+ // {{{ pushErrorHandling()
677
+
678
+ /**
679
+ * Push a new error handler on top of the error handler options stack. With this
680
+ * you can easily override the actual error handler for some code and restore
681
+ * it later with popErrorHandling.
682
+ *
683
+ * @param mixed $mode (same as setErrorHandling)
684
+ * @param mixed $options (same as setErrorHandling)
685
+ *
686
+ * @return bool Always true
687
+ *
688
+ * @see PEAR::setErrorHandling
689
+ */
690
+ function pushErrorHandling($mode, $options = null)
691
+ {
692
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
693
+ if (isset($this) && is_a($this, 'PEAR')) {
694
+ $def_mode = &$this->_default_error_mode;
695
+ $def_options = &$this->_default_error_options;
696
+ } else {
697
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
698
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
699
+ }
700
+ $stack[] = array($def_mode, $def_options);
701
+
702
+ if (isset($this) && is_a($this, 'PEAR')) {
703
+ $this->setErrorHandling($mode, $options);
704
+ } else {
705
+ PEAR::setErrorHandling($mode, $options);
706
+ }
707
+ $stack[] = array($mode, $options);
708
+ return true;
709
+ }
710
+
711
+ // }}}
712
+ // {{{ popErrorHandling()
713
+
714
+ /**
715
+ * Pop the last error handler used
716
+ *
717
+ * @return bool Always true
718
+ *
719
+ * @see PEAR::pushErrorHandling
720
+ */
721
+ function popErrorHandling()
722
+ {
723
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
724
+ array_pop($stack);
725
+ list($mode, $options) = $stack[sizeof($stack) - 1];
726
+ array_pop($stack);
727
+ if (isset($this) && is_a($this, 'PEAR')) {
728
+ $this->setErrorHandling($mode, $options);
729
+ } else {
730
+ PEAR::setErrorHandling($mode, $options);
731
+ }
732
+ return true;
733
+ }
734
+
735
+ // }}}
736
+ // {{{ loadExtension()
737
+
738
+ /**
739
+ * OS independant PHP extension load. Remember to take care
740
+ * on the correct extension name for case sensitive OSes.
741
+ *
742
+ * @param string $ext The extension name
743
+ * @return bool Success or not on the dl() call
744
+ */
745
+ function loadExtension($ext)
746
+ {
747
+ if (!extension_loaded($ext)) {
748
+ // if either returns true dl() will produce a FATAL error, stop that
749
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
750
+ return false;
751
+ }
752
+ if (OS_WINDOWS) {
753
+ $suffix = '.dll';
754
+ } elseif (PHP_OS == 'HP-UX') {
755
+ $suffix = '.sl';
756
+ } elseif (PHP_OS == 'AIX') {
757
+ $suffix = '.a';
758
+ } elseif (PHP_OS == 'OSX') {
759
+ $suffix = '.bundle';
760
+ } else {
761
+ $suffix = '.so';
762
+ }
763
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
764
+ }
765
+ return true;
766
+ }
767
+
768
+ // }}}
769
+ }
770
+
771
+ // {{{ _PEAR_call_destructors()
772
+
773
+ function _PEAR_call_destructors()
774
+ {
775
+ global $_PEAR_destructor_object_list;
776
+ if (is_array($_PEAR_destructor_object_list) &&
777
+ sizeof($_PEAR_destructor_object_list))
778
+ {
779
+ reset($_PEAR_destructor_object_list);
780
+ if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
781
+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
782
+ }
783
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
784
+ $classname = get_class($objref);
785
+ while ($classname) {
786
+ $destructor = "_$classname";
787
+ if (method_exists($objref, $destructor)) {
788
+ $objref->$destructor();
789
+ break;
790
+ } else {
791
+ $classname = get_parent_class($classname);
792
+ }
793
+ }
794
+ }
795
+ // Empty the object list to ensure that destructors are
796
+ // not called more than once.
797
+ $_PEAR_destructor_object_list = array();
798
+ }
799
+
800
+ // Now call the shutdown functions
801
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
802
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
803
+ call_user_func_array($value[0], $value[1]);
804
+ }
805
+ }
806
+ }
807
+
808
+ // }}}
809
+ /**
810
+ * Standard PEAR error class for PHP 4
811
+ *
812
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
813
+ *
814
+ * @category pear
815
+ * @package PEAR
816
+ * @author Stig Bakken <ssb@php.net>
817
+ * @author Tomas V.V. Cox <cox@idecnet.com>
818
+ * @author Gregory Beaver <cellog@php.net>
819
+ * @copyright 1997-2006 The PHP Group
820
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
821
+ * @version Release: 1.7.2
822
+ * @link http://pear.php.net/manual/en/core.pear.pear-error.php
823
+ * @see PEAR::raiseError(), PEAR::throwError()
824
+ * @since Class available since PHP 4.0.2
825
+ */
826
+ class PEAR_Error
827
+ {
828
+ // {{{ properties
829
+
830
+ var $error_message_prefix = '';
831
+ var $mode = PEAR_ERROR_RETURN;
832
+ var $level = E_USER_NOTICE;
833
+ var $code = -1;
834
+ var $message = '';
835
+ var $userinfo = '';
836
+ var $backtrace = null;
837
+
838
+ // }}}
839
+ // {{{ constructor
840
+
841
+ /**
842
+ * PEAR_Error constructor
843
+ *
844
+ * @param string $message message
845
+ *
846
+ * @param int $code (optional) error code
847
+ *
848
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
849
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
850
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
851
+ *
852
+ * @param mixed $options (optional) error level, _OR_ in the case of
853
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
854
+ * tuple.
855
+ *
856
+ * @param string $userinfo (optional) additional user/debug info
857
+ *
858
+ * @access public
859
+ *
860
+ */
861
+ function PEAR_Error($message = 'unknown error', $code = null,
862
+ $mode = null, $options = null, $userinfo = null)
863
+ {
864
+ if ($mode === null) {
865
+ $mode = PEAR_ERROR_RETURN;
866
+ }
867
+ $this->message = $message;
868
+ $this->code = $code;
869
+ $this->mode = $mode;
870
+ $this->userinfo = $userinfo;
871
+ if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
872
+ $this->backtrace = debug_backtrace();
873
+ if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
874
+ unset($this->backtrace[0]['object']);
875
+ }
876
+ }
877
+ if ($mode & PEAR_ERROR_CALLBACK) {
878
+ $this->level = E_USER_NOTICE;
879
+ $this->callback = $options;
880
+ } else {
881
+ if ($options === null) {
882
+ $options = E_USER_NOTICE;
883
+ }
884
+ $this->level = $options;
885
+ $this->callback = null;
886
+ }
887
+ if ($this->mode & PEAR_ERROR_PRINT) {
888
+ if (is_null($options) || is_int($options)) {
889
+ $format = "%s";
890
+ } else {
891
+ $format = $options;
892
+ }
893
+ printf($format, $this->getMessage());
894
+ }
895
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
896
+ trigger_error($this->getMessage(), $this->level);
897
+ }
898
+ if ($this->mode & PEAR_ERROR_DIE) {
899
+ $msg = $this->getMessage();
900
+ if (is_null($options) || is_int($options)) {
901
+ $format = "%s";
902
+ if (substr($msg, -1) != "\n") {
903
+ $msg .= "\n";
904
+ }
905
+ } else {
906
+ $format = $options;
907
+ }
908
+ die(sprintf($format, $msg));
909
+ }
910
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
911
+ if (is_callable($this->callback)) {
912
+ call_user_func($this->callback, $this);
913
+ }
914
+ }
915
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
916
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
917
+ eval('$e = new Exception($this->message, $this->code);throw($e);');
918
+ }
919
+ }
920
+
921
+ // }}}
922
+ // {{{ getMode()
923
+
924
+ /**
925
+ * Get the error mode from an error object.
926
+ *
927
+ * @return int error mode
928
+ * @access public
929
+ */
930
+ function getMode() {
931
+ return $this->mode;
932
+ }
933
+
934
+ // }}}
935
+ // {{{ getCallback()
936
+
937
+ /**
938
+ * Get the callback function/method from an error object.
939
+ *
940
+ * @return mixed callback function or object/method array
941
+ * @access public
942
+ */
943
+ function getCallback() {
944
+ return $this->callback;
945
+ }
946
+
947
+ // }}}
948
+ // {{{ getMessage()
949
+
950
+
951
+ /**
952
+ * Get the error message from an error object.
953
+ *
954
+ * @return string full error message
955
+ * @access public
956
+ */
957
+ function getMessage()
958
+ {
959
+ return ($this->error_message_prefix . $this->message);
960
+ }
961
+
962
+
963
+ // }}}
964
+ // {{{ getCode()
965
+
966
+ /**
967
+ * Get error code from an error object
968
+ *
969
+ * @return int error code
970
+ * @access public
971
+ */
972
+ function getCode()
973
+ {
974
+ return $this->code;
975
+ }
976
+
977
+ // }}}
978
+ // {{{ getType()
979
+
980
+ /**
981
+ * Get the name of this error/exception.
982
+ *
983
+ * @return string error/exception name (type)
984
+ * @access public
985
+ */
986
+ function getType()
987
+ {
988
+ return get_class($this);
989
+ }
990
+
991
+ // }}}
992
+ // {{{ getUserInfo()
993
+
994
+ /**
995
+ * Get additional user-supplied information.
996
+ *
997
+ * @return string user-supplied information
998
+ * @access public
999
+ */
1000
+ function getUserInfo()
1001
+ {
1002
+ return $this->userinfo;
1003
+ }
1004
+
1005
+ // }}}
1006
+ // {{{ getDebugInfo()
1007
+
1008
+ /**
1009
+ * Get additional debug information supplied by the application.
1010
+ *
1011
+ * @return string debug information
1012
+ * @access public
1013
+ */
1014
+ function getDebugInfo()
1015
+ {
1016
+ return $this->getUserInfo();
1017
+ }
1018
+
1019
+ // }}}
1020
+ // {{{ getBacktrace()
1021
+
1022
+ /**
1023
+ * Get the call backtrace from where the error was generated.
1024
+ * Supported with PHP 4.3.0 or newer.
1025
+ *
1026
+ * @param int $frame (optional) what frame to fetch
1027
+ * @return array Backtrace, or NULL if not available.
1028
+ * @access public
1029
+ */
1030
+ function getBacktrace($frame = null)
1031
+ {
1032
+ if (defined('PEAR_IGNORE_BACKTRACE')) {
1033
+ return null;
1034
+ }
1035
+ if ($frame === null) {
1036
+ return $this->backtrace;
1037
+ }
1038
+ return $this->backtrace[$frame];
1039
+ }
1040
+
1041
+ // }}}
1042
+ // {{{ addUserInfo()
1043
+
1044
+ function addUserInfo($info)
1045
+ {
1046
+ if (empty($this->userinfo)) {
1047
+ $this->userinfo = $info;
1048
+ } else {
1049
+ $this->userinfo .= " ** $info";
1050
+ }
1051
+ }
1052
+
1053
+ // }}}
1054
+ // {{{ toString()
1055
+ function __toString()
1056
+ {
1057
+ return $this->getMessage();
1058
+ }
1059
+ // }}}
1060
+ // {{{ toString()
1061
+
1062
+ /**
1063
+ * Make a string representation of this object.
1064
+ *
1065
+ * @return string a string with an object summary
1066
+ * @access public
1067
+ */
1068
+ function toString() {
1069
+ $modes = array();
1070
+ $levels = array(E_USER_NOTICE => 'notice',
1071
+ E_USER_WARNING => 'warning',
1072
+ E_USER_ERROR => 'error');
1073
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
1074
+ if (is_array($this->callback)) {
1075
+ $callback = (is_object($this->callback[0]) ?
1076
+ strtolower(get_class($this->callback[0])) :
1077
+ $this->callback[0]) . '::' .
1078
+ $this->callback[1];
1079
+ } else {
1080
+ $callback = $this->callback;
1081
+ }
1082
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
1083
+ 'callback=%s prefix="%s" info="%s"]',
1084
+ strtolower(get_class($this)), $this->message, $this->code,
1085
+ $callback, $this->error_message_prefix,
1086
+ $this->userinfo);
1087
+ }
1088
+ if ($this->mode & PEAR_ERROR_PRINT) {
1089
+ $modes[] = 'print';
1090
+ }
1091
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
1092
+ $modes[] = 'trigger';
1093
+ }
1094
+ if ($this->mode & PEAR_ERROR_DIE) {
1095
+ $modes[] = 'die';
1096
+ }
1097
+ if ($this->mode & PEAR_ERROR_RETURN) {
1098
+ $modes[] = 'return';
1099
+ }
1100
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
1101
+ 'prefix="%s" info="%s"]',
1102
+ strtolower(get_class($this)), $this->message, $this->code,
1103
+ implode("|", $modes), $levels[$this->level],
1104
+ $this->error_message_prefix,
1105
+ $this->userinfo);
1106
+ }
1107
+
1108
+ // }}}
1109
+ }
1110
+
1111
+ /*
1112
+ * Local Variables:
1113
+ * mode: php
1114
+ * tab-width: 4
1115
+ * c-basic-offset: 4
1116
+ * End:
1117
+ */
1118
+ ?>
lib/PEAR/PEAR/PEAR.php ADDED
@@ -0,0 +1,1118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PEAR, the PHP Extension and Application Repository
4
+ *
5
+ * PEAR class and PEAR_Error class
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
10
+ * that is available through the world-wide-web at the following URI:
11
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
12
+ * the PHP License and are unable to obtain it through the web, please
13
+ * send a note to license@php.net so we can mail you a copy immediately.
14
+ *
15
+ * @category pear
16
+ * @package PEAR
17
+ * @author Sterling Hughes <sterling@php.net>
18
+ * @author Stig Bakken <ssb@php.net>
19
+ * @author Tomas V.V.Cox <cox@idecnet.com>
20
+ * @author Greg Beaver <cellog@php.net>
21
+ * @copyright 1997-2008 The PHP Group
22
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
23
+ * @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
24
+ * @link http://pear.php.net/package/PEAR
25
+ * @since File available since Release 0.1
26
+ */
27
+
28
+ /**#@+
29
+ * ERROR constants
30
+ */
31
+ define('PEAR_ERROR_RETURN', 1);
32
+ define('PEAR_ERROR_PRINT', 2);
33
+ define('PEAR_ERROR_TRIGGER', 4);
34
+ define('PEAR_ERROR_DIE', 8);
35
+ define('PEAR_ERROR_CALLBACK', 16);
36
+ /**
37
+ * WARNING: obsolete
38
+ * @deprecated
39
+ */
40
+ define('PEAR_ERROR_EXCEPTION', 32);
41
+ /**#@-*/
42
+ define('PEAR_ZE2', (function_exists('version_compare') &&
43
+ version_compare(zend_version(), "2-dev", "ge")));
44
+
45
+ if (substr(PHP_OS, 0, 3) == 'WIN') {
46
+ define('OS_WINDOWS', true);
47
+ define('OS_UNIX', false);
48
+ define('PEAR_OS', 'Windows');
49
+ } else {
50
+ define('OS_WINDOWS', false);
51
+ define('OS_UNIX', true);
52
+ define('PEAR_OS', 'Unix'); // blatant assumption
53
+ }
54
+
55
+ // instant backwards compatibility
56
+ if (!defined('PATH_SEPARATOR')) {
57
+ if (OS_WINDOWS) {
58
+ define('PATH_SEPARATOR', ';');
59
+ } else {
60
+ define('PATH_SEPARATOR', ':');
61
+ }
62
+ }
63
+
64
+ $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
65
+ $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
66
+ $GLOBALS['_PEAR_destructor_object_list'] = array();
67
+ $GLOBALS['_PEAR_shutdown_funcs'] = array();
68
+ $GLOBALS['_PEAR_error_handler_stack'] = array();
69
+
70
+ @ini_set('track_errors', true);
71
+
72
+ /**
73
+ * Base class for other PEAR classes. Provides rudimentary
74
+ * emulation of destructors.
75
+ *
76
+ * If you want a destructor in your class, inherit PEAR and make a
77
+ * destructor method called _yourclassname (same name as the
78
+ * constructor, but with a "_" prefix). Also, in your constructor you
79
+ * have to call the PEAR constructor: $this->PEAR();.
80
+ * The destructor method will be called without parameters. Note that
81
+ * at in some SAPI implementations (such as Apache), any output during
82
+ * the request shutdown (in which destructors are called) seems to be
83
+ * discarded. If you need to get any debug information from your
84
+ * destructor, use error_log(), syslog() or something similar.
85
+ *
86
+ * IMPORTANT! To use the emulated destructors you need to create the
87
+ * objects by reference: $obj =& new PEAR_child;
88
+ *
89
+ * @category pear
90
+ * @package PEAR
91
+ * @author Stig Bakken <ssb@php.net>
92
+ * @author Tomas V.V. Cox <cox@idecnet.com>
93
+ * @author Greg Beaver <cellog@php.net>
94
+ * @copyright 1997-2006 The PHP Group
95
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
96
+ * @version Release: 1.7.2
97
+ * @link http://pear.php.net/package/PEAR
98
+ * @see PEAR_Error
99
+ * @since Class available since PHP 4.0.2
100
+ * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
101
+ */
102
+ class PEAR
103
+ {
104
+ // {{{ properties
105
+
106
+ /**
107
+ * Whether to enable internal debug messages.
108
+ *
109
+ * @var bool
110
+ * @access private
111
+ */
112
+ var $_debug = false;
113
+
114
+ /**
115
+ * Default error mode for this object.
116
+ *
117
+ * @var int
118
+ * @access private
119
+ */
120
+ var $_default_error_mode = null;
121
+
122
+ /**
123
+ * Default error options used for this object when error mode
124
+ * is PEAR_ERROR_TRIGGER.
125
+ *
126
+ * @var int
127
+ * @access private
128
+ */
129
+ var $_default_error_options = null;
130
+
131
+ /**
132
+ * Default error handler (callback) for this object, if error mode is
133
+ * PEAR_ERROR_CALLBACK.
134
+ *
135
+ * @var string
136
+ * @access private
137
+ */
138
+ var $_default_error_handler = '';
139
+
140
+ /**
141
+ * Which class to use for error objects.
142
+ *
143
+ * @var string
144
+ * @access private
145
+ */
146
+ var $_error_class = 'PEAR_Error';
147
+
148
+ /**
149
+ * An array of expected errors.
150
+ *
151
+ * @var array
152
+ * @access private
153
+ */
154
+ var $_expected_errors = array();
155
+
156
+ // }}}
157
+
158
+ // {{{ constructor
159
+
160
+ /**
161
+ * Constructor. Registers this object in
162
+ * $_PEAR_destructor_object_list for destructor emulation if a
163
+ * destructor object exists.
164
+ *
165
+ * @param string $error_class (optional) which class to use for
166
+ * error objects, defaults to PEAR_Error.
167
+ * @access public
168
+ * @return void
169
+ */
170
+ function PEAR($error_class = null)
171
+ {
172
+ $classname = strtolower(get_class($this));
173
+ if ($this->_debug) {
174
+ print "PEAR constructor called, class=$classname\n";
175
+ }
176
+ if ($error_class !== null) {
177
+ $this->_error_class = $error_class;
178
+ }
179
+ while ($classname && strcasecmp($classname, "pear")) {
180
+ $destructor = "_$classname";
181
+ if (method_exists($this, $destructor)) {
182
+ global $_PEAR_destructor_object_list;
183
+ $_PEAR_destructor_object_list[] = &$this;
184
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
185
+ register_shutdown_function("_PEAR_call_destructors");
186
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
187
+ }
188
+ break;
189
+ } else {
190
+ $classname = get_parent_class($classname);
191
+ }
192
+ }
193
+ }
194
+
195
+ // }}}
196
+ // {{{ destructor
197
+
198
+ /**
199
+ * Destructor (the emulated type of...). Does nothing right now,
200
+ * but is included for forward compatibility, so subclass
201
+ * destructors should always call it.
202
+ *
203
+ * See the note in the class desciption about output from
204
+ * destructors.
205
+ *
206
+ * @access public
207
+ * @return void
208
+ */
209
+ function _PEAR() {
210
+ if ($this->_debug) {
211
+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
212
+ }
213
+ }
214
+
215
+ // }}}
216
+ // {{{ getStaticProperty()
217
+
218
+ /**
219
+ * If you have a class that's mostly/entirely static, and you need static
220
+ * properties, you can use this method to simulate them. Eg. in your method(s)
221
+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
222
+ * You MUST use a reference, or they will not persist!
223
+ *
224
+ * @access public
225
+ * @param string $class The calling classname, to prevent clashes
226
+ * @param string $var The variable to retrieve.
227
+ * @return mixed A reference to the variable. If not set it will be
228
+ * auto initialised to NULL.
229
+ */
230
+ public static function &getStaticProperty($class, $var)
231
+ {
232
+ static $properties;
233
+ if (!isset($properties[$class])) {
234
+ $properties[$class] = array();
235
+ }
236
+ if (!array_key_exists($var, $properties[$class])) {
237
+ $properties[$class][$var] = null;
238
+ }
239
+ return $properties[$class][$var];
240
+ }
241
+
242
+ // }}}
243
+ // {{{ registerShutdownFunc()
244
+
245
+ /**
246
+ * Use this function to register a shutdown method for static
247
+ * classes.
248
+ *
249
+ * @access public
250
+ * @param mixed $func The function name (or array of class/method) to call
251
+ * @param mixed $args The arguments to pass to the function
252
+ * @return void
253
+ */
254
+ function registerShutdownFunc($func, $args = array())
255
+ {
256
+ // if we are called statically, there is a potential
257
+ // that no shutdown func is registered. Bug #6445
258
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
259
+ register_shutdown_function("_PEAR_call_destructors");
260
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
261
+ }
262
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
263
+ }
264
+
265
+ // }}}
266
+ // {{{ isError()
267
+
268
+ /**
269
+ * Tell whether a value is a PEAR error.
270
+ *
271
+ * @param mixed $data the value to test
272
+ * @param int $code if $data is an error object, return true
273
+ * only if $code is a string and
274
+ * $obj->getMessage() == $code or
275
+ * $code is an integer and $obj->getCode() == $code
276
+ * @access public
277
+ * @return bool true if parameter is an error
278
+ */
279
+ public static function isError($data, $code = null)
280
+ {
281
+ if ($data instanceof PEAR_Error) {
282
+ if (is_null($code)) {
283
+ return true;
284
+ } elseif (is_string($code)) {
285
+ return $data->getMessage() == $code;
286
+ } else {
287
+ return $data->getCode() == $code;
288
+ }
289
+ }
290
+ return false;
291
+ }
292
+
293
+ // }}}
294
+ // {{{ setErrorHandling()
295
+
296
+ /**
297
+ * Sets how errors generated by this object should be handled.
298
+ * Can be invoked both in objects and statically. If called
299
+ * statically, setErrorHandling sets the default behaviour for all
300
+ * PEAR objects. If called in an object, setErrorHandling sets
301
+ * the default behaviour for that object.
302
+ *
303
+ * @param int $mode
304
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
305
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
306
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
307
+ *
308
+ * @param mixed $options
309
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
310
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
311
+ *
312
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
313
+ * to be the callback function or method. A callback
314
+ * function is a string with the name of the function, a
315
+ * callback method is an array of two elements: the element
316
+ * at index 0 is the object, and the element at index 1 is
317
+ * the name of the method to call in the object.
318
+ *
319
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
320
+ * a printf format string used when printing the error
321
+ * message.
322
+ *
323
+ * @access public
324
+ * @return void
325
+ * @see PEAR_ERROR_RETURN
326
+ * @see PEAR_ERROR_PRINT
327
+ * @see PEAR_ERROR_TRIGGER
328
+ * @see PEAR_ERROR_DIE
329
+ * @see PEAR_ERROR_CALLBACK
330
+ * @see PEAR_ERROR_EXCEPTION
331
+ *
332
+ * @since PHP 4.0.5
333
+ */
334
+
335
+ function setErrorHandling($mode = null, $options = null)
336
+ {
337
+ if (isset($this) && is_a($this, 'PEAR')) {
338
+ $setmode = &$this->_default_error_mode;
339
+ $setoptions = &$this->_default_error_options;
340
+ } else {
341
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
342
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
343
+ }
344
+
345
+ switch ($mode) {
346
+ case PEAR_ERROR_EXCEPTION:
347
+ case PEAR_ERROR_RETURN:
348
+ case PEAR_ERROR_PRINT:
349
+ case PEAR_ERROR_TRIGGER:
350
+ case PEAR_ERROR_DIE:
351
+ case null:
352
+ $setmode = $mode;
353
+ $setoptions = $options;
354
+ break;
355
+
356
+ case PEAR_ERROR_CALLBACK:
357
+ $setmode = $mode;
358
+ // class/object method callback
359
+ if (is_callable($options)) {
360
+ $setoptions = $options;
361
+ } else {
362
+ trigger_error("invalid error callback", E_USER_WARNING);
363
+ }
364
+ break;
365
+
366
+ default:
367
+ trigger_error("invalid error mode", E_USER_WARNING);
368
+ break;
369
+ }
370
+ }
371
+
372
+ // }}}
373
+ // {{{ expectError()
374
+
375
+ /**
376
+ * This method is used to tell which errors you expect to get.
377
+ * Expected errors are always returned with error mode
378
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
379
+ * and this method pushes a new element onto it. The list of
380
+ * expected errors are in effect until they are popped off the
381
+ * stack with the popExpect() method.
382
+ *
383
+ * Note that this method can not be called statically
384
+ *
385
+ * @param mixed $code a single error code or an array of error codes to expect
386
+ *
387
+ * @return int the new depth of the "expected errors" stack
388
+ * @access public
389
+ */
390
+ function expectError($code = '*')
391
+ {
392
+ if (is_array($code)) {
393
+ array_push($this->_expected_errors, $code);
394
+ } else {
395
+ array_push($this->_expected_errors, array($code));
396
+ }
397
+ return sizeof($this->_expected_errors);
398
+ }
399
+
400
+ // }}}
401
+ // {{{ popExpect()
402
+
403
+ /**
404
+ * This method pops one element off the expected error codes
405
+ * stack.
406
+ *
407
+ * @return array the list of error codes that were popped
408
+ */
409
+ function popExpect()
410
+ {
411
+ return array_pop($this->_expected_errors);
412
+ }
413
+
414
+ // }}}
415
+ // {{{ _checkDelExpect()
416
+
417
+ /**
418
+ * This method checks unsets an error code if available
419
+ *
420
+ * @param mixed error code
421
+ * @return bool true if the error code was unset, false otherwise
422
+ * @access private
423
+ * @since PHP 4.3.0
424
+ */
425
+ function _checkDelExpect($error_code)
426
+ {
427
+ $deleted = false;
428
+
429
+ foreach ($this->_expected_errors AS $key => $error_array) {
430
+ if (in_array($error_code, $error_array)) {
431
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
432
+ $deleted = true;
433
+ }
434
+
435
+ // clean up empty arrays
436
+ if (0 == count($this->_expected_errors[$key])) {
437
+ unset($this->_expected_errors[$key]);
438
+ }
439
+ }
440
+ return $deleted;
441
+ }
442
+
443
+ // }}}
444
+ // {{{ delExpect()
445
+
446
+ /**
447
+ * This method deletes all occurences of the specified element from
448
+ * the expected error codes stack.
449
+ *
450
+ * @param mixed $error_code error code that should be deleted
451
+ * @return mixed list of error codes that were deleted or error
452
+ * @access public
453
+ * @since PHP 4.3.0
454
+ */
455
+ function delExpect($error_code)
456
+ {
457
+ $deleted = false;
458
+
459
+ if ((is_array($error_code) && (0 != count($error_code)))) {
460
+ // $error_code is a non-empty array here;
461
+ // we walk through it trying to unset all
462
+ // values
463
+ foreach($error_code as $key => $error) {
464
+ if ($this->_checkDelExpect($error)) {
465
+ $deleted = true;
466
+ } else {
467
+ $deleted = false;
468
+ }
469
+ }
470
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
471
+ } elseif (!empty($error_code)) {
472
+ // $error_code comes alone, trying to unset it
473
+ if ($this->_checkDelExpect($error_code)) {
474
+ return true;
475
+ } else {
476
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
477
+ }
478
+ } else {
479
+ // $error_code is empty
480
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
481
+ }
482
+ }
483
+
484
+ // }}}
485
+ // {{{ raiseError()
486
+
487
+ /**
488
+ * This method is a wrapper that returns an instance of the
489
+ * configured error class with this object's default error
490
+ * handling applied. If the $mode and $options parameters are not
491
+ * specified, the object's defaults are used.
492
+ *
493
+ * @param mixed $message a text error message or a PEAR error object
494
+ *
495
+ * @param int $code a numeric error code (it is up to your class
496
+ * to define these if you want to use codes)
497
+ *
498
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
499
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
500
+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
501
+ *
502
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
503
+ * specifies the PHP-internal error level (one of
504
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
505
+ * If $mode is PEAR_ERROR_CALLBACK, this
506
+ * parameter specifies the callback function or
507
+ * method. In other error modes this parameter
508
+ * is ignored.
509
+ *
510
+ * @param string $userinfo If you need to pass along for example debug
511
+ * information, this parameter is meant for that.
512
+ *
513
+ * @param string $error_class The returned error object will be
514
+ * instantiated from this class, if specified.
515
+ *
516
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
517
+ * the error message parameter will be dropped.
518
+ *
519
+ * @access public
520
+ * @return object a PEAR error object
521
+ * @see PEAR::setErrorHandling
522
+ * @since PHP 4.0.5
523
+ */
524
+ public static function raiseError($message = null,
525
+ $code = null,
526
+ $mode = null,
527
+ $options = null,
528
+ $userinfo = null,
529
+ $error_class = null,
530
+ $skipmsg = false)
531
+ {
532
+ // The error is yet a PEAR error object
533
+ if (is_object($message)) {
534
+ $code = $message->getCode();
535
+ $userinfo = $message->getUserInfo();
536
+ $error_class = $message->getType();
537
+ $message->error_message_prefix = '';
538
+ $message = $message->getMessage();
539
+ }
540
+
541
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
542
+ if ($exp[0] == "*" ||
543
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
544
+ (is_string(reset($exp)) && in_array($message, $exp))) {
545
+ $mode = PEAR_ERROR_RETURN;
546
+ }
547
+ }
548
+ // No mode given, try global ones
549
+ if ($mode === null) {
550
+ // Class error handler
551
+ if (isset($this) && isset($this->_default_error_mode)) {
552
+ $mode = $this->_default_error_mode;
553
+ $options = $this->_default_error_options;
554
+ // Global error handler
555
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
556
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
557
+ $options = $GLOBALS['_PEAR_default_error_options'];
558
+ }
559
+ }
560
+
561
+ if ($error_class !== null) {
562
+ $ec = $error_class;
563
+ } elseif (isset($this) && isset($this->_error_class)) {
564
+ $ec = $this->_error_class;
565
+ } else {
566
+ $ec = 'PEAR_Error';
567
+ }
568
+ if (intval(PHP_VERSION) < 5) {
569
+ // little non-eval hack to fix bug #12147
570
+ include 'PEAR/FixPHP5PEARWarnings.php';
571
+ return $a;
572
+ }
573
+ if ($skipmsg) {
574
+ $a = new $ec($code, $mode, $options, $userinfo);
575
+ } else {
576
+ $a = new $ec($message, $code, $mode, $options, $userinfo);
577
+ }
578
+ return $a;
579
+ }
580
+
581
+ // }}}
582
+ // {{{ throwError()
583
+
584
+ /**
585
+ * Simpler form of raiseError with fewer options. In most cases
586
+ * message, code and userinfo are enough.
587
+ *
588
+ * @param string $message
589
+ *
590
+ */
591
+ function &throwError($message = null,
592
+ $code = null,
593
+ $userinfo = null)
594
+ {
595
+ if (isset($this) && is_a($this, 'PEAR')) {
596
+ $a = &$this->raiseError($message, $code, null, null, $userinfo);
597
+ return $a;
598
+ } else {
599
+ $a = &PEAR::raiseError($message, $code, null, null, $userinfo);
600
+ return $a;
601
+ }
602
+ }
603
+
604
+ // }}}
605
+ function staticPushErrorHandling($mode, $options = null)
606
+ {
607
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
608
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
609
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
610
+ $stack[] = array($def_mode, $def_options);
611
+ switch ($mode) {
612
+ case PEAR_ERROR_EXCEPTION:
613
+ case PEAR_ERROR_RETURN:
614
+ case PEAR_ERROR_PRINT:
615
+ case PEAR_ERROR_TRIGGER:
616
+ case PEAR_ERROR_DIE:
617
+ case null:
618
+ $def_mode = $mode;
619
+ $def_options = $options;
620
+ break;
621
+
622
+ case PEAR_ERROR_CALLBACK:
623
+ $def_mode = $mode;
624
+ // class/object method callback
625
+ if (is_callable($options)) {
626
+ $def_options = $options;
627
+ } else {
628
+ trigger_error("invalid error callback", E_USER_WARNING);
629
+ }
630
+ break;
631
+
632
+ default:
633
+ trigger_error("invalid error mode", E_USER_WARNING);
634
+ break;
635
+ }
636
+ $stack[] = array($mode, $options);
637
+ return true;
638
+ }
639
+
640
+ function staticPopErrorHandling()
641
+ {
642
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
643
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
644
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
645
+ array_pop($stack);
646
+ list($mode, $options) = $stack[sizeof($stack) - 1];
647
+ array_pop($stack);
648
+ switch ($mode) {
649
+ case PEAR_ERROR_EXCEPTION:
650
+ case PEAR_ERROR_RETURN:
651
+ case PEAR_ERROR_PRINT:
652
+ case PEAR_ERROR_TRIGGER:
653
+ case PEAR_ERROR_DIE:
654
+ case null:
655
+ $setmode = $mode;
656
+ $setoptions = $options;
657
+ break;
658
+
659
+ case PEAR_ERROR_CALLBACK:
660
+ $setmode = $mode;
661
+ // class/object method callback
662
+ if (is_callable($options)) {
663
+ $setoptions = $options;
664
+ } else {
665
+ trigger_error("invalid error callback", E_USER_WARNING);
666
+ }
667
+ break;
668
+
669
+ default:
670
+ trigger_error("invalid error mode", E_USER_WARNING);
671
+ break;
672
+ }
673
+ return true;
674
+ }
675
+
676
+ // {{{ pushErrorHandling()
677
+
678
+ /**
679
+ * Push a new error handler on top of the error handler options stack. With this
680
+ * you can easily override the actual error handler for some code and restore
681
+ * it later with popErrorHandling.
682
+ *
683
+ * @param mixed $mode (same as setErrorHandling)
684
+ * @param mixed $options (same as setErrorHandling)
685
+ *
686
+ * @return bool Always true
687
+ *
688
+ * @see PEAR::setErrorHandling
689
+ */
690
+ function pushErrorHandling($mode, $options = null)
691
+ {
692
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
693
+ if (isset($this) && is_a($this, 'PEAR')) {
694
+ $def_mode = &$this->_default_error_mode;
695
+ $def_options = &$this->_default_error_options;
696
+ } else {
697
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
698
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
699
+ }
700
+ $stack[] = array($def_mode, $def_options);
701
+
702
+ if (isset($this) && is_a($this, 'PEAR')) {
703
+ $this->setErrorHandling($mode, $options);
704
+ } else {
705
+ PEAR::setErrorHandling($mode, $options);
706
+ }
707
+ $stack[] = array($mode, $options);
708
+ return true;
709
+ }
710
+
711
+ // }}}
712
+ // {{{ popErrorHandling()
713
+
714
+ /**
715
+ * Pop the last error handler used
716
+ *
717
+ * @return bool Always true
718
+ *
719
+ * @see PEAR::pushErrorHandling
720
+ */
721
+ function popErrorHandling()
722
+ {
723
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
724
+ array_pop($stack);
725
+ list($mode, $options) = $stack[sizeof($stack) - 1];
726
+ array_pop($stack);
727
+ if (isset($this) && is_a($this, 'PEAR')) {
728
+ $this->setErrorHandling($mode, $options);
729
+ } else {
730
+ PEAR::setErrorHandling($mode, $options);
731
+ }
732
+ return true;
733
+ }
734
+
735
+ // }}}
736
+ // {{{ loadExtension()
737
+
738
+ /**
739
+ * OS independant PHP extension load. Remember to take care
740
+ * on the correct extension name for case sensitive OSes.
741
+ *
742
+ * @param string $ext The extension name
743
+ * @return bool Success or not on the dl() call
744
+ */
745
+ function loadExtension($ext)
746
+ {
747
+ if (!extension_loaded($ext)) {
748
+ // if either returns true dl() will produce a FATAL error, stop that
749
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
750
+ return false;
751
+ }
752
+ if (OS_WINDOWS) {
753
+ $suffix = '.dll';
754
+ } elseif (PHP_OS == 'HP-UX') {
755
+ $suffix = '.sl';
756
+ } elseif (PHP_OS == 'AIX') {
757
+ $suffix = '.a';
758
+ } elseif (PHP_OS == 'OSX') {
759
+ $suffix = '.bundle';
760
+ } else {
761
+ $suffix = '.so';
762
+ }
763
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
764
+ }
765
+ return true;
766
+ }
767
+
768
+ // }}}
769
+ }
770
+
771
+ // {{{ _PEAR_call_destructors()
772
+
773
+ function _PEAR_call_destructors()
774
+ {
775
+ global $_PEAR_destructor_object_list;
776
+ if (is_array($_PEAR_destructor_object_list) &&
777
+ sizeof($_PEAR_destructor_object_list))
778
+ {
779
+ reset($_PEAR_destructor_object_list);
780
+ if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
781
+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
782
+ }
783
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
784
+ $classname = get_class($objref);
785
+ while ($classname) {
786
+ $destructor = "_$classname";
787
+ if (method_exists($objref, $destructor)) {
788
+ $objref->$destructor();
789
+ break;
790
+ } else {
791
+ $classname = get_parent_class($classname);
792
+ }
793
+ }
794
+ }
795
+ // Empty the object list to ensure that destructors are
796
+ // not called more than once.
797
+ $_PEAR_destructor_object_list = array();
798
+ }
799
+
800
+ // Now call the shutdown functions
801
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
802
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
803
+ call_user_func_array($value[0], $value[1]);
804
+ }
805
+ }
806
+ }
807
+
808
+ // }}}
809
+ /**
810
+ * Standard PEAR error class for PHP 4
811
+ *
812
+ * This class is supserseded by {@link PEAR_Exception} in PHP 5
813
+ *
814
+ * @category pear
815
+ * @package PEAR
816
+ * @author Stig Bakken <ssb@php.net>
817
+ * @author Tomas V.V. Cox <cox@idecnet.com>
818
+ * @author Gregory Beaver <cellog@php.net>
819
+ * @copyright 1997-2006 The PHP Group
820
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
821
+ * @version Release: 1.7.2
822
+ * @link http://pear.php.net/manual/en/core.pear.pear-error.php
823
+ * @see PEAR::raiseError(), PEAR::throwError()
824
+ * @since Class available since PHP 4.0.2
825
+ */
826
+ class PEAR_Error
827
+ {
828
+ // {{{ properties
829
+
830
+ var $error_message_prefix = '';
831
+ var $mode = PEAR_ERROR_RETURN;
832
+ var $level = E_USER_NOTICE;
833
+ var $code = -1;
834
+ var $message = '';
835
+ var $userinfo = '';
836
+ var $backtrace = null;
837
+
838
+ // }}}
839
+ // {{{ constructor
840
+
841
+ /**
842
+ * PEAR_Error constructor
843
+ *
844
+ * @param string $message message
845
+ *
846
+ * @param int $code (optional) error code
847
+ *
848
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
849
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
850
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
851
+ *
852
+ * @param mixed $options (optional) error level, _OR_ in the case of
853
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
854
+ * tuple.
855
+ *
856
+ * @param string $userinfo (optional) additional user/debug info
857
+ *
858
+ * @access public
859
+ *
860
+ */
861
+ function PEAR_Error($message = 'unknown error', $code = null,
862
+ $mode = null, $options = null, $userinfo = null)
863
+ {
864
+ if ($mode === null) {
865
+ $mode = PEAR_ERROR_RETURN;
866
+ }
867
+ $this->message = $message;
868
+ $this->code = $code;
869
+ $this->mode = $mode;
870
+ $this->userinfo = $userinfo;
871
+ if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
872
+ $this->backtrace = debug_backtrace();
873
+ if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
874
+ unset($this->backtrace[0]['object']);
875
+ }
876
+ }
877
+ if ($mode & PEAR_ERROR_CALLBACK) {
878
+ $this->level = E_USER_NOTICE;
879
+ $this->callback = $options;
880
+ } else {
881
+ if ($options === null) {
882
+ $options = E_USER_NOTICE;
883
+ }
884
+ $this->level = $options;
885
+ $this->callback = null;
886
+ }
887
+ if ($this->mode & PEAR_ERROR_PRINT) {
888
+ if (is_null($options) || is_int($options)) {
889
+ $format = "%s";
890
+ } else {
891
+ $format = $options;
892
+ }
893
+ printf($format, $this->getMessage());
894
+ }
895
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
896
+ trigger_error($this->getMessage(), $this->level);
897
+ }
898
+ if ($this->mode & PEAR_ERROR_DIE) {
899
+ $msg = $this->getMessage();
900
+ if (is_null($options) || is_int($options)) {
901
+ $format = "%s";
902
+ if (substr($msg, -1) != "\n") {
903
+ $msg .= "\n";
904
+ }
905
+ } else {
906
+ $format = $options;
907
+ }
908
+ die(sprintf($format, $msg));
909
+ }
910
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
911
+ if (is_callable($this->callback)) {
912
+ call_user_func($this->callback, $this);
913
+ }
914
+ }
915
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
916
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
917
+ eval('$e = new Exception($this->message, $this->code);throw($e);');
918
+ }
919
+ }
920
+
921
+ // }}}
922
+ // {{{ getMode()
923
+
924
+ /**
925
+ * Get the error mode from an error object.
926
+ *
927
+ * @return int error mode
928
+ * @access public
929
+ */
930
+ function getMode() {
931
+ return $this->mode;
932
+ }
933
+
934
+ // }}}
935
+ // {{{ getCallback()
936
+
937
+ /**
938
+ * Get the callback function/method from an error object.
939
+ *
940
+ * @return mixed callback function or object/method array
941
+ * @access public
942
+ */
943
+ function getCallback() {
944
+ return $this->callback;
945
+ }
946
+
947
+ // }}}
948
+ // {{{ getMessage()
949
+
950
+
951
+ /**
952
+ * Get the error message from an error object.
953
+ *
954
+ * @return string full error message
955
+ * @access public
956
+ */
957
+ function getMessage()
958
+ {
959
+ return ($this->error_message_prefix . $this->message);
960
+ }
961
+
962
+
963
+ // }}}
964
+ // {{{ getCode()
965
+
966
+ /**
967
+ * Get error code from an error object
968
+ *
969
+ * @return int error code
970
+ * @access public
971
+ */
972
+ function getCode()
973
+ {
974
+ return $this->code;
975
+ }
976
+
977
+ // }}}
978
+ // {{{ getType()
979
+
980
+ /**
981
+ * Get the name of this error/exception.
982
+ *
983
+ * @return string error/exception name (type)
984
+ * @access public
985
+ */
986
+ function getType()
987
+ {
988
+ return get_class($this);
989
+ }
990
+
991
+ // }}}
992
+ // {{{ getUserInfo()
993
+
994
+ /**
995
+ * Get additional user-supplied information.
996
+ *
997
+ * @return string user-supplied information
998
+ * @access public
999
+ */
1000
+ function getUserInfo()
1001
+ {
1002
+ return $this->userinfo;
1003
+ }
1004
+
1005
+ // }}}
1006
+ // {{{ getDebugInfo()
1007
+
1008
+ /**
1009
+ * Get additional debug information supplied by the application.
1010
+ *
1011
+ * @return string debug information
1012
+ * @access public
1013
+ */
1014
+ function getDebugInfo()
1015
+ {
1016
+ return $this->getUserInfo();
1017
+ }
1018
+
1019
+ // }}}
1020
+ // {{{ getBacktrace()
1021
+
1022
+ /**
1023
+ * Get the call backtrace from where the error was generated.
1024
+ * Supported with PHP 4.3.0 or newer.
1025
+ *
1026
+ * @param int $frame (optional) what frame to fetch
1027
+ * @return array Backtrace, or NULL if not available.
1028
+ * @access public
1029
+ */
1030
+ function getBacktrace($frame = null)
1031
+ {
1032
+ if (defined('PEAR_IGNORE_BACKTRACE')) {
1033
+ return null;
1034
+ }
1035
+ if ($frame === null) {
1036
+ return $this->backtrace;
1037
+ }
1038
+ return $this->backtrace[$frame];
1039
+ }
1040
+
1041
+ // }}}
1042
+ // {{{ addUserInfo()
1043
+
1044
+ function addUserInfo($info)
1045
+ {
1046
+ if (empty($this->userinfo)) {
1047
+ $this->userinfo = $info;
1048
+ } else {
1049
+ $this->userinfo .= " ** $info";
1050
+ }
1051
+ }
1052
+
1053
+ // }}}
1054
+ // {{{ toString()
1055
+ function __toString()
1056
+ {
1057
+ return $this->getMessage();
1058
+ }
1059
+ // }}}
1060
+ // {{{ toString()
1061
+
1062
+ /**
1063
+ * Make a string representation of this object.
1064
+ *
1065
+ * @return string a string with an object summary
1066
+ * @access public
1067
+ */
1068
+ function toString() {
1069
+ $modes = array();
1070
+ $levels = array(E_USER_NOTICE => 'notice',
1071
+ E_USER_WARNING => 'warning',
1072
+ E_USER_ERROR => 'error');
1073
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
1074
+ if (is_array($this->callback)) {
1075
+ $callback = (is_object($this->callback[0]) ?
1076
+ strtolower(get_class($this->callback[0])) :
1077
+ $this->callback[0]) . '::' .
1078
+ $this->callback[1];
1079
+ } else {
1080
+ $callback = $this->callback;
1081
+ }
1082
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
1083
+ 'callback=%s prefix="%s" info="%s"]',
1084
+ strtolower(get_class($this)), $this->message, $this->code,
1085
+ $callback, $this->error_message_prefix,
1086
+ $this->userinfo);
1087
+ }
1088
+ if ($this->mode & PEAR_ERROR_PRINT) {
1089
+ $modes[] = 'print';
1090
+ }
1091
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
1092
+ $modes[] = 'trigger';
1093
+ }
1094
+ if ($this->mode & PEAR_ERROR_DIE) {
1095
+ $modes[] = 'die';
1096
+ }
1097
+ if ($this->mode & PEAR_ERROR_RETURN) {
1098
+ $modes[] = 'return';
1099
+ }
1100
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
1101
+ 'prefix="%s" info="%s"]',
1102
+ strtolower(get_class($this)), $this->message, $this->code,
1103
+ implode("|", $modes), $levels[$this->level],
1104
+ $this->error_message_prefix,
1105
+ $this->userinfo);
1106
+ }
1107
+
1108
+ // }}}
1109
+ }
1110
+
1111
+ /*
1112
+ * Local Variables:
1113
+ * mode: php
1114
+ * tab-width: 4
1115
+ * c-basic-offset: 4
1116
+ * End:
1117
+ */
1118
+ ?>
lib/PEAR/SOAP/Base.php ADDED
@@ -0,0 +1,1142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file loads all required libraries, defines constants used across the
4
+ * SOAP package, and defines the base classes that most other classes of this
5
+ * package extend.
6
+ *
7
+ * PHP versions 4 and 5
8
+ *
9
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
10
+ * that is bundled with this package in the file LICENSE, and is available at
11
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
12
+ * did not receive a copy of the PHP license and are unable to obtain it
13
+ * through the world-wide-web, please send a note to license@php.net so we can
14
+ * mail you a copy immediately.
15
+ *
16
+ * @category Web Services
17
+ * @package SOAP
18
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
19
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
20
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
21
+ * @author Jan Schneider <jan@horde.org> Maintenance
22
+ * @copyright 2003-2007 The PHP Group
23
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
24
+ * @link http://pear.php.net/package/SOAP
25
+ */
26
+
27
+ /** Define linebreak sequence for the Mail_Mime package. */
28
+ define('MAIL_MIMEPART_CRLF', "\r\n");
29
+
30
+ require_once 'PEAR.php';
31
+
32
+ if (!defined('INF')) {
33
+ define('INF', 1.8e307);
34
+ }
35
+ if (!defined('NAN')) {
36
+ define('NAN', 0.0);
37
+ }
38
+
39
+ define('SOAP_LIBRARY_VERSION', '0.12.0');
40
+ define('SOAP_LIBRARY_NAME', 'PEAR-SOAP 0.12.0-beta');
41
+
42
+ // Set schema version.
43
+ define('SOAP_XML_SCHEMA_VERSION', 'http://www.w3.org/2001/XMLSchema');
44
+ define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
45
+ define('SOAP_XML_SCHEMA_1999', 'http://www.w3.org/1999/XMLSchema');
46
+ define('SOAP_SCHEMA', 'http://schemas.xmlsoap.org/wsdl/soap/');
47
+ define('SOAP_SCHEMA_ENCODING', 'http://schemas.xmlsoap.org/soap/encoding/');
48
+ define('SOAP_ENVELOP', 'http://schemas.xmlsoap.org/soap/envelope/');
49
+
50
+ define('SCHEMA_DISCO', 'http://schemas.xmlsoap.org/disco/');
51
+ define('SCHEMA_DISCO_SCL', 'http://schemas.xmlsoap.org/disco/scl/');
52
+
53
+ define('SCHEMA_SOAP', 'http://schemas.xmlsoap.org/wsdl/soap/');
54
+ define('SCHEMA_SOAP12', 'http://schemas.xmlsoap.org/wsdl/soap12/');
55
+ define('SCHEMA_SOAP_HTTP', 'http://schemas.xmlsoap.org/soap/http');
56
+ define('SCHEMA_WSDL_HTTP', 'http://schemas.xmlsoap.org/wsdl/http/');
57
+ define('SCHEMA_MIME', 'http://schemas.xmlsoap.org/wsdl/mime/');
58
+ define('SCHEMA_WSDL', 'http://schemas.xmlsoap.org/wsdl/');
59
+ define('SCHEMA_DIME', 'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
60
+ define('SCHEMA_CONTENT', 'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
61
+ define('SCHEMA_REF', 'http://schemas.xmlsoap.org/ws/2002/04/reference/');
62
+
63
+ define('SOAP_DEFAULT_ENCODING', 'UTF-8');
64
+
65
+ /**
66
+ * @package SOAP
67
+ */
68
+ class SOAP_Base_Object extends PEAR
69
+ {
70
+
71
+ /**
72
+ * Supported encodings, limited by XML extension.
73
+ *
74
+ * @var array $_encodings
75
+ */
76
+ var $_encodings = array('ISO-8859-1', 'US-ASCII', 'UTF-8');
77
+
78
+ /**
79
+ * Fault code.
80
+ *
81
+ * @var string $_myfaultcode
82
+ */
83
+ var $_myfaultcode = '';
84
+
85
+ /**
86
+ * Recent PEAR_Error object.
87
+ *
88
+ * @var PEAR_Error $fault
89
+ */
90
+ var $fault = null;
91
+
92
+ /**
93
+ * Constructor.
94
+ *
95
+ * @param string $faultcode Error code.
96
+ */
97
+ function SOAP_Base_Object($faultcode = 'Client')
98
+ {
99
+ $this->_myfaultcode = $faultcode;
100
+ parent::PEAR('SOAP_Fault');
101
+ }
102
+
103
+ /**
104
+ * Raises a SOAP error.
105
+ *
106
+ * Please refer to the SOAP definition for an impression of what a certain
107
+ * parameter stands for.
108
+ *
109
+ * @param string|object $str Error message or object.
110
+ * @param string $detail Detailed error message.
111
+ * @param string $actorURI
112
+ * @param mixed $code
113
+ * @param mixed $mode
114
+ * @param mixed $options
115
+ * @param boolean $skipmsg
116
+ */
117
+ function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null,
118
+ $mode = null, $options = null, $skipmsg = false)
119
+ {
120
+ // Pass through previous faults.
121
+ $is_instance = isset($this) && $this instanceof SOAP_Base_Object;
122
+ if (is_object($str)) {
123
+ $fault = $str;
124
+ } else {
125
+ if (!$code) {
126
+ $code = $is_instance ? $this->_myfaultcode : 'Client';
127
+ }
128
+ require_once 'SOAP/Fault.php';
129
+ $fault = new SOAP_Fault($str, $code, $actorURI, $detail, $mode,
130
+ $options);
131
+ }
132
+ if ($is_instance) {
133
+ $this->fault = $fault;
134
+ }
135
+
136
+ return $fault;
137
+ }
138
+
139
+ function _isfault()
140
+ {
141
+ return $this->fault != null;
142
+ }
143
+
144
+ function &_getfault()
145
+ {
146
+ return $this->fault;
147
+ }
148
+
149
+ }
150
+
151
+ /**
152
+ * Common base class of all SOAP classes.
153
+ *
154
+ * @access public
155
+ * @package SOAP
156
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
157
+ */
158
+ class SOAP_Base extends SOAP_Base_Object
159
+ {
160
+ var $_XMLSchema = array('http://www.w3.org/2001/XMLSchema',
161
+ 'http://www.w3.org/1999/XMLSchema');
162
+ var $_XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
163
+
164
+ // load types into typemap array
165
+ var $_typemap = array(
166
+ 'http://www.w3.org/2001/XMLSchema' => array(
167
+ 'string' => 'string',
168
+ 'boolean' => 'boolean',
169
+ 'float' => 'float',
170
+ 'double' => 'float',
171
+ 'decimal' => 'float',
172
+ 'duration' => 'integer',
173
+ 'dateTime' => 'string',
174
+ 'time' => 'string',
175
+ 'date' => 'string',
176
+ 'gYearMonth' => 'integer',
177
+ 'gYear' => 'integer',
178
+ 'gMonthDay' => 'integer',
179
+ 'gDay' => 'integer',
180
+ 'gMonth' => 'integer',
181
+ 'hexBinary' => 'string',
182
+ 'base64Binary' => 'string',
183
+ // derived datatypes
184
+ 'normalizedString' => 'string',
185
+ 'token' => 'string',
186
+ 'language' => 'string',
187
+ 'NMTOKEN' => 'string',
188
+ 'NMTOKENS' => 'string',
189
+ 'Name' => 'string',
190
+ 'NCName' => 'string',
191
+ 'ID' => 'string',
192
+ 'IDREF' => 'string',
193
+ 'IDREFS' => 'string',
194
+ 'ENTITY' => 'string',
195
+ 'ENTITIES' => 'string',
196
+ 'integer' => 'integer',
197
+ 'nonPositiveInteger' => 'integer',
198
+ 'negativeInteger' => 'integer',
199
+ // longs (64bit ints) are not supported cross-platform.
200
+ 'long' => 'string',
201
+ 'int' => 'integer',
202
+ 'short' => 'integer',
203
+ 'byte' => 'string',
204
+ 'nonNegativeInteger' => 'integer',
205
+ 'unsignedLong' => 'integer',
206
+ 'unsignedInt' => 'integer',
207
+ 'unsignedShort' => 'integer',
208
+ 'unsignedByte' => 'integer',
209
+ 'positiveInteger' => 'integer',
210
+ 'anyType' => 'string',
211
+ 'anyURI' => 'string',
212
+ 'QName' => 'string'
213
+ ),
214
+ 'http://www.w3.org/1999/XMLSchema' => array(
215
+ 'i4' => 'integer',
216
+ 'int' => 'integer',
217
+ 'boolean' => 'boolean',
218
+ 'string' => 'string',
219
+ 'double' => 'float',
220
+ 'float' => 'float',
221
+ 'dateTime' => 'string',
222
+ 'timeInstant' => 'string',
223
+ 'base64Binary' => 'string',
224
+ 'base64' => 'string',
225
+ 'ur-type' => 'string'
226
+ ),
227
+ 'http://schemas.xmlsoap.org/soap/encoding/' => array(
228
+ 'base64' => 'string',
229
+ 'array' => 'array',
230
+ 'Array' => 'array',
231
+ 'Struct' => 'array')
232
+ );
233
+
234
+ /**
235
+ * Default class name to use for decoded response objects.
236
+ *
237
+ * @var string $_defaultObjectClassname
238
+ */
239
+ var $_defaultObjectClassname = 'stdClass';
240
+
241
+ /**
242
+ * Hash with used namespaces.
243
+ *
244
+ * @var array
245
+ */
246
+ var $_namespaces;
247
+
248
+ /**
249
+ * The default namespace.
250
+ *
251
+ * @var string
252
+ */
253
+ var $_namespace;
254
+
255
+ var $_xmlEntities = array('&' => '&amp;',
256
+ '<' => '&lt;',
257
+ '>' => '&gt;',
258
+ "'" => '&apos;',
259
+ '"' => '&quot;');
260
+
261
+ var $_doconversion = false;
262
+
263
+ var $_attachments = array();
264
+
265
+ var $_wsdl = null;
266
+
267
+ /**
268
+ * True if we use section 5 encoding, or false if this is literal.
269
+ *
270
+ * @var boolean $_section5
271
+ */
272
+ var $_section5 = true;
273
+
274
+ // Handle type to class mapping.
275
+ var $_auto_translation = false;
276
+ var $_type_translation = array();
277
+
278
+ /**
279
+ * Constructor.
280
+ *
281
+ * @param string $faultcode Error code.
282
+ */
283
+ function SOAP_Base($faultcode = 'Client')
284
+ {
285
+ parent::SOAP_Base_Object($faultcode);
286
+ $this->_resetNamespaces();
287
+ }
288
+
289
+ /**
290
+ * Sets the SOAP-ENV prefix and returns the current value.
291
+ *
292
+ * @access public
293
+ *
294
+ * @param string SOAP-ENV prefix
295
+ *
296
+ * @return string current SOAP-ENV prefix.
297
+ */
298
+ function SOAPENVPrefix($prefix = null)
299
+ {
300
+ static $_soapenv_prefix = 'SOAP-ENV';
301
+ if (!is_null($prefix)) {
302
+ $_soapenv_prefix = $prefix;
303
+ }
304
+ return $_soapenv_prefix;
305
+ }
306
+
307
+ /**
308
+ * Sets the SOAP-ENC prefix and returns the current value.
309
+ *
310
+ * @access public
311
+ *
312
+ * @param string SOAP-ENC prefix
313
+ *
314
+ * @return string current SOAP-ENC prefix.
315
+ */
316
+ function SOAPENCPrefix($prefix = null)
317
+ {
318
+ static $_soapenv_prefix = 'SOAP-ENC';
319
+ if (!is_null($prefix)) {
320
+ $_soapenv_prefix = $prefix;
321
+ }
322
+ return $_soapenv_prefix;
323
+ }
324
+
325
+ /**
326
+ * Sets the default namespace.
327
+ *
328
+ * @param string $namespace The default namespace.
329
+ */
330
+ function setDefaultNamespace($namespace)
331
+ {
332
+ $this->_namespace = $namespace;
333
+ }
334
+
335
+ function _resetNamespaces()
336
+ {
337
+ $this->_namespaces = array(
338
+ 'http://schemas.xmlsoap.org/soap/envelope/' => SOAP_BASE::SOAPENVPrefix(),
339
+ 'http://www.w3.org/2001/XMLSchema' => 'xsd',
340
+ 'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
341
+ 'http://schemas.xmlsoap.org/soap/encoding/' => SOAP_BASE::SOAPENCPrefix());
342
+ }
343
+
344
+ /**
345
+ * Sets the schema version used in the SOAP message.
346
+ *
347
+ * @access private
348
+ * @see $_XMLSchema
349
+ *
350
+ * @param string $schemaVersion The schema version.
351
+ */
352
+ function _setSchemaVersion($schemaVersion)
353
+ {
354
+ if (!in_array($schemaVersion, $this->_XMLSchema)) {
355
+ return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
356
+ }
357
+ $this->_XMLSchemaVersion = $schemaVersion;
358
+ $tmpNS = array_flip($this->_namespaces);
359
+ $tmpNS['xsd'] = $this->_XMLSchemaVersion;
360
+ $tmpNS['xsi'] = $this->_XMLSchemaVersion . '-instance';
361
+ $this->_namespaces = array_flip($tmpNS);
362
+ }
363
+
364
+ function _getNamespacePrefix($ns)
365
+ {
366
+ if ($this->_namespace && $ns == $this->_namespace) {
367
+ return '';
368
+ }
369
+ if (isset($this->_namespaces[$ns])) {
370
+ return $this->_namespaces[$ns];
371
+ }
372
+ $prefix = 'ns' . count($this->_namespaces);
373
+ $this->_namespaces[$ns] = $prefix;
374
+ return $prefix;
375
+ }
376
+
377
+ function _getNamespaceForPrefix($prefix)
378
+ {
379
+ $flipped = array_flip($this->_namespaces);
380
+ if (isset($flipped[$prefix])) {
381
+ return $flipped[$prefix];
382
+ }
383
+ return null;
384
+ }
385
+
386
+ /**
387
+ * Serializes a value, array or object according to the rules set by this
388
+ * object.
389
+ *
390
+ * @see SOAP_Value
391
+ *
392
+ * @param mixed $value The actual value.
393
+ * @param QName $name The value name.
394
+ * @param QName $type The value type.
395
+ * @param array $options A list of encoding and serialization options.
396
+ * @param array $attributes A hash of additional attributes.
397
+ * @param string $artype The type of any array elements.
398
+ */
399
+ function _serializeValue($value, $name = null, $type = null,
400
+ $options = array(), $attributes = array(),
401
+ $artype = '')
402
+ {
403
+ $namespaces = array();
404
+ $arrayType = $array_depth = $xmlout_value = null;
405
+ $typePrefix = $elPrefix = $xmlout_arrayType = '';
406
+ $xmlout_type = $xmlns = $ptype = $array_type_ns = '';
407
+
408
+ if (!$name->name || is_numeric($name->name)) {
409
+ $name->name = 'item';
410
+ }
411
+
412
+ if ($this->_wsdl) {
413
+ list($ptype, $arrayType, $array_type_ns, $array_depth)
414
+ = $this->_wsdl->getSchemaType($type, $name);
415
+ }
416
+
417
+ if (!$arrayType) {
418
+ $arrayType = $artype;
419
+ }
420
+ if (!$ptype) {
421
+ $ptype = $this->_getType($value);
422
+ }
423
+ if (!$type) {
424
+ $type = new QName($ptype);
425
+ }
426
+
427
+ if (strcasecmp($ptype, 'Struct') == 0 ||
428
+ strcasecmp($type->name, 'Struct') == 0) {
429
+ // Struct
430
+ $vars = is_object($value) ? get_object_vars($value) : $value;
431
+ if (is_array($vars)) {
432
+ foreach (array_keys($vars) as $k) {
433
+ // Hide private vars.
434
+ if ($k[0] == '_') {
435
+ continue;
436
+ }
437
+
438
+ if (is_object($vars[$k])) {
439
+ if (is_a($vars[$k], 'SOAP_Value')) {
440
+ $xmlout_value .= $vars[$k]->serialize($this);
441
+ } else {
442
+ // XXX get the members and serialize them instead
443
+ // converting to an array is more overhead than we
444
+ // should really do.
445
+ $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), new QName($k, $this->_section5 ? null : $name->namepace), null, $options);
446
+ }
447
+ } else {
448
+ $xmlout_value .= $this->_serializeValue($vars[$k], new QName($k, $this->_section5 ? null : $name->namespace), false, $options);
449
+ }
450
+ }
451
+ }
452
+ } elseif (strcasecmp($ptype, 'Array') == 0 ||
453
+ strcasecmp($type->name, 'Array') == 0) {
454
+ // Array.
455
+ $type = new QName('Array', SOAP_SCHEMA_ENCODING);
456
+ $numtypes = 0;
457
+ $value = (array)$value;
458
+ // XXX this will be slow on larger arrays. Basically, it flattens
459
+ // arrays to allow us to serialize multi-dimensional arrays. We
460
+ // only do this if arrayType is set, which will typically only
461
+ // happen if we are using WSDL
462
+ if (isset($options['flatten']) ||
463
+ ($arrayType &&
464
+ (strchr($arrayType, ',') || strstr($arrayType, '][')))) {
465
+ $numtypes = $this->_multiArrayType($value, $arrayType,
466
+ $ar_size, $xmlout_value);
467
+ }
468
+
469
+ $array_type = $array_type_prefix = '';
470
+ if ($numtypes != 1) {
471
+ $arrayTypeQName = new QName($arrayType);
472
+ $arrayType = $arrayTypeQName->name;
473
+ $array_types = array();
474
+ $array_val = null;
475
+
476
+ // Serialize each array element.
477
+ $ar_size = count($value);
478
+ foreach ($value as $array_val) {
479
+ if (is_a($array_val, 'SOAP_Value')) {
480
+ $array_type = $array_val->type;
481
+ $array_types[$array_type] = 1;
482
+ $array_type_ns = $array_val->type_namespace;
483
+ $xmlout_value .= $array_val->serialize($this);
484
+ } else {
485
+ $array_type = $this->_getType($array_val);
486
+ $array_types[$array_type] = 1;
487
+ if (empty($options['keep_arrays_flat'])) {
488
+ $xmlout_value .= $this->_serializeValue($array_val, new QName('item', $this->_section5 ? null : $name->namespace), new QName($array_type), $options);
489
+ } else {
490
+ $xmlout_value .= $this->_serializeValue($array_val, $name, new QName($array_type), $options, $attributes);
491
+ }
492
+ }
493
+ }
494
+
495
+ if (!$arrayType) {
496
+ $numtypes = count($array_types);
497
+ if ($numtypes == 1) {
498
+ $arrayType = $array_type;
499
+ }
500
+ // Using anyType is more interoperable.
501
+ if ($array_type == 'Struct') {
502
+ $array_type = '';
503
+ } elseif ($array_type == 'Array') {
504
+ $arrayType = 'anyType';
505
+ $array_type_prefix = 'xsd';
506
+ } else {
507
+ if (!$arrayType) {
508
+ $arrayType = $array_type;
509
+ }
510
+ }
511
+ }
512
+ }
513
+ if (!$arrayType || $numtypes > 1) {
514
+ // Should reference what schema we're using.
515
+ $arrayType = 'xsd:anyType';
516
+ } else {
517
+ if ($array_type_ns) {
518
+ $array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
519
+ } elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) {
520
+ $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
521
+ } elseif (isset($this->_typemap[SOAP_SCHEMA_ENCODING][$arrayType])) {
522
+ $array_type_prefix = SOAP_BASE::SOAPENCPrefix();
523
+ }
524
+ if ($array_type_prefix) {
525
+ $arrayType = $array_type_prefix . ':' . $arrayType;
526
+ }
527
+ }
528
+
529
+ $xmlout_arrayType = ' ' . SOAP_BASE::SOAPENCPrefix()
530
+ . ':arrayType="' . $arrayType;
531
+ if ($array_depth != null) {
532
+ for ($i = 0; $i < $array_depth; $i++) {
533
+ $xmlout_arrayType .= '[]';
534
+ }
535
+ }
536
+ $xmlout_arrayType .= "[$ar_size]\"";
537
+ } elseif ($value instanceof SOAP_Value) {
538
+ $xmlout_value = $value->serialize($this);
539
+ } elseif ($type->name == 'string') {
540
+ $xmlout_value = htmlspecialchars($value);
541
+ } elseif ($type->name == 'rawstring') {
542
+ $xmlout_value = $value;
543
+ } elseif ($type->name == 'boolean') {
544
+ $xmlout_value = $value ? 'true' : 'false';
545
+ } else {
546
+ $xmlout_value = $value;
547
+ }
548
+
549
+ // Add namespaces.
550
+ if ($name->namespace) {
551
+ $elPrefix = $this->_getNamespacePrefix($name->namespace);
552
+ if ($elPrefix) {
553
+ $xmlout_name = $elPrefix . ':' . $name->name;
554
+ } else {
555
+ $xmlout_name = $name->name;
556
+ }
557
+ } else {
558
+ $xmlout_name = $name->name;
559
+ }
560
+
561
+ if ($type->namespace) {
562
+ $typePrefix = false;
563
+ if (empty($options['no_type_prefix'])) {
564
+ $typePrefix = $this->_getNamespacePrefix($type->namespace);
565
+ }
566
+ if ($typePrefix) {
567
+ $xmlout_type = $typePrefix . ':' . $type->name;
568
+ } else {
569
+ $xmlout_type = $type->name;
570
+ }
571
+ } elseif ($type->name &&
572
+ isset($this->_typemap[$this->_XMLSchemaVersion][$type->name])) {
573
+ $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
574
+ if ($typePrefix) {
575
+ $xmlout_type = $typePrefix . ':' . $type->name;
576
+ } else {
577
+ $xmlout_type = $type->name;
578
+ }
579
+ }
580
+
581
+ // Handle additional attributes.
582
+ $xml_attr = '';
583
+ if (count($attributes)) {
584
+ foreach ($attributes as $k => $v) {
585
+ $kqn = new QName($k);
586
+ $vqn = new QName($v);
587
+ $xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"';
588
+ }
589
+ }
590
+
591
+ // Store the attachment for mime encoding.
592
+ if (isset($options['attachment']) &&
593
+ !PEAR::isError($options['attachment'])) {
594
+ $this->_attachments[] = $options['attachment'];
595
+ }
596
+
597
+ if ($this->_section5) {
598
+ if ($xmlout_type) {
599
+ $xmlout_type = " xsi:type=\"$xmlout_type\"";
600
+ }
601
+ if (is_null($xmlout_value)) {
602
+ $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
603
+ "$xml_attr xsi:nil=\"true\"/>";
604
+ } else {
605
+ $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
606
+ "$xml_attr>$xmlout_value</$xmlout_name>";
607
+ }
608
+ } elseif ($type->name == 'Array' && !empty($options['keep_arrays_flat'])) {
609
+ $xml = $xmlout_value;
610
+ } else {
611
+ if (is_null($xmlout_value)) {
612
+ $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
613
+ } else {
614
+ $xml = "\r\n<$xmlout_name$xmlns$xml_attr>" .
615
+ $xmlout_value . "</$xmlout_name>";
616
+ }
617
+ }
618
+
619
+ return $xml;
620
+ }
621
+
622
+ /**
623
+ * Converts a PHP type to a SOAP type.
624
+ *
625
+ * @param mixed $value The value to inspect.
626
+ *
627
+ * @return string The value's SOAP type.
628
+ */
629
+ function _getType($value)
630
+ {
631
+ $type = gettype($value);
632
+ switch ($type) {
633
+ case 'object':
634
+ if (is_a($value, 'soap_value')) {
635
+ $type = $value->type;
636
+ } else {
637
+ $type = 'Struct';
638
+ }
639
+ break;
640
+
641
+ case 'array':
642
+ // Hashes are always handled as structs.
643
+ if ($this->_isHash($value)) {
644
+ $type = 'Struct';
645
+ break;
646
+ }
647
+ if (count($value) > 1) {
648
+ // For non-wsdl structs that are all the same type
649
+ reset($value);
650
+ $value1 = next($value);
651
+ $value2 = next($value);
652
+ if (is_a($value1, 'SOAP_Value') &&
653
+ is_a($value2, 'SOAP_Value') &&
654
+ $value1->name != $value2->name) {
655
+ // This is a struct, not an array.
656
+ $type = 'Struct';
657
+ break;
658
+ }
659
+ }
660
+ $type = 'Array';
661
+ break;
662
+
663
+ case 'integer':
664
+ case 'long':
665
+ $type = 'int';
666
+ break;
667
+
668
+ case 'boolean':
669
+ break;
670
+
671
+ case 'double':
672
+ // double is deprecated in PHP 4.2 and later.
673
+ $type = 'float';
674
+ break;
675
+
676
+ case 'null':
677
+ $type = '';
678
+ break;
679
+
680
+ case 'string':
681
+ default:
682
+ break;
683
+ }
684
+
685
+ return $type;
686
+ }
687
+
688
+ function _multiArrayType($value, &$type, &$size, &$xml)
689
+ {
690
+ if (is_array($value)) {
691
+ // Seems we have a multi dimensional array, figure it out if we
692
+ // do.
693
+ for ($i = 0, $c = count($value); $i < $c; ++$i) {
694
+ $this->_multiArrayType($value[$i], $type, $size, $xml);
695
+ }
696
+
697
+ $sz = count($value);
698
+ if ($size) {
699
+ $size = $sz . ',' . $size;
700
+ } else {
701
+ $size = $sz;
702
+ }
703
+ return 1;
704
+ } elseif (is_object($value)) {
705
+ $type = $value->type;
706
+ $xml .= $value->serialize($this);
707
+ } else {
708
+ $type = $this->_getType($value);
709
+ $xml .= $this->_serializeValue($value, new QName('item'), new QName($type));
710
+ }
711
+ $size = null;
712
+
713
+ return 1;
714
+ }
715
+
716
+ /**
717
+ * Returns whether a type is a base64 type.
718
+ *
719
+ * @param string $type A type name.
720
+ *
721
+ * @return boolean True if the type name is a base64 type.
722
+ */
723
+ function _isBase64Type($type)
724
+ {
725
+ return $type == 'base64' || $type == 'base64Binary';
726
+ }
727
+
728
+ /**
729
+ * Returns whether an array is a hash.
730
+ *
731
+ * @param array $a An array to check.
732
+ *
733
+ * @return boolean True if the specified array is a hash.
734
+ */
735
+ function _isHash($a)
736
+ {
737
+ foreach (array_keys($a) as $k) {
738
+ // Checking the type is faster than regexp.
739
+ if (!is_int($k)) {
740
+ return true;
741
+ }
742
+ }
743
+ return false;
744
+ }
745
+
746
+ function _un_htmlentities($string)
747
+ {
748
+ $trans_tbl = get_html_translation_table(HTML_ENTITIES);
749
+ $trans_tbl = array_flip($trans_tbl);
750
+ return strtr($string, $trans_tbl);
751
+ }
752
+
753
+ /**
754
+ * Converts a SOAP_Value object into a PHP value.
755
+ */
756
+ function _decode($soapval)
757
+ {
758
+ if (!$soapval instanceof SOAP_Value) {
759
+ return $soapval;
760
+ }
761
+
762
+ if (is_array($soapval->value)) {
763
+ $isstruct = $soapval->type != 'Array';
764
+ if ($isstruct) {
765
+ $classname = $this->_defaultObjectClassname;
766
+ if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
767
+ // This will force an error in PHP if the class does not
768
+ // exist.
769
+ $classname = $this->_type_translation[$soapval->tqn->fqn()];
770
+ } elseif (isset($this->_type_translation[$soapval->type])) {
771
+ // This will force an error in PHP if the class does not
772
+ // exist.
773
+ $classname = $this->_type_translation[$soapval->type];
774
+ } elseif ($this->_auto_translation) {
775
+ if (class_exists($soapval->type)) {
776
+ $classname = $soapval->type;
777
+ } elseif ($this->_wsdl) {
778
+ $t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
779
+ if ($t && class_exists($t)) {
780
+ $classname = $t;
781
+ }
782
+ }
783
+ }
784
+ $return = new $classname;
785
+ } else {
786
+ $return = array();
787
+ }
788
+
789
+ foreach ($soapval->value as $item) {
790
+ if ($isstruct) {
791
+ if ($this->_wsdl) {
792
+ // Get this child's WSDL information.
793
+ // /$soapval->ns/$soapval->type/$item->ns/$item->name
794
+ $child_type = $this->_wsdl->getComplexTypeChildType(
795
+ $soapval->namespace,
796
+ $soapval->name,
797
+ $item->namespace,
798
+ $item->name);
799
+ if ($child_type) {
800
+ $item->type = $child_type;
801
+ }
802
+ }
803
+ if ($item->type == 'Array') {
804
+ if (isset($return->{$item->name}) &&
805
+ is_object($return->{$item->name})) {
806
+ $return->{$item->name} = $this->_decode($item);
807
+ } elseif (isset($return->{$item->name}) &&
808
+ is_array($return->{$item->name})) {
809
+ $return->{$item->name}[] = $this->_decode($item);
810
+ } elseif (isset($return->{$item->name})) {
811
+ $return->{$item->name} = array(
812
+ $return->{$item->name},
813
+ $this->_decode($item)
814
+ );
815
+ } elseif (is_array($return)) {
816
+ $return[] = $this->_decode($item);
817
+ } else {
818
+ $return->{$item->name} = $this->_decode($item);
819
+ }
820
+ } elseif (isset($return->{$item->name})) {
821
+ $d = $this->_decode($item);
822
+ if (count(get_object_vars($return)) == 1) {
823
+ $isstruct = false;
824
+ $return = array($return->{$item->name}, $d);
825
+ } else {
826
+ $return->{$item->name} = array($return->{$item->name}, $d);
827
+ }
828
+ } else {
829
+ $return->{$item->name} = $this->_decode($item);
830
+ }
831
+ // Set the attributes as members in the class.
832
+ if (method_exists($return, '__set_attribute')) {
833
+ foreach ($soapval->attributes as $key => $value) {
834
+ call_user_func_array(array(&$return,
835
+ '__set_attribute'),
836
+ array($key, $value));
837
+ }
838
+ }
839
+ } else {
840
+ if ($soapval->arrayType && is_a($item, 'SOAP_Value')) {
841
+ if ($this->_isBase64Type($item->type) &&
842
+ !$this->_isBase64Type($soapval->arrayType)) {
843
+ // Decode the value if we're losing the base64
844
+ // type information.
845
+ $item->value = base64_decode($item->value);
846
+ }
847
+ $item->type = $soapval->arrayType;
848
+ }
849
+ $return[] = $this->_decode($item);
850
+ }
851
+ }
852
+
853
+ return $return;
854
+ }
855
+
856
+ if ($soapval->type == 'boolean') {
857
+ if ($soapval->value != '0' &&
858
+ strcasecmp($soapval->value, 'false') != 0) {
859
+ $soapval->value = true;
860
+ } else {
861
+ $soapval->value = false;
862
+ }
863
+ } elseif ($soapval->type &&
864
+ isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type])) {
865
+ // If we can, set variable type.
866
+ settype($soapval->value,
867
+ $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
868
+ } elseif ($soapval->type == 'Struct') {
869
+ $soapval->value = null;
870
+ }
871
+
872
+ return $soapval->value;
873
+ }
874
+
875
+ /**
876
+ * Creates the SOAP envelope with the SOAP envelop data.
877
+ *
878
+ * @param SOAP_Value $method SOAP_Value instance with the method name as
879
+ * the name, and the method arguments as the
880
+ * value.
881
+ * @param array $headers A list of additional SOAP_Header objects.
882
+ * @param string $encoding The charset of the SOAP message.
883
+ * @param array $options A list of encoding/serialization options.
884
+ *
885
+ * @return string The complete SOAP message.
886
+ */
887
+ function makeEnvelope($method, $headers, $encoding = SOAP_DEFAULT_ENCODING,
888
+ $options = array())
889
+ {
890
+ $smsg = $header_xml = $ns_string = '';
891
+
892
+ if ($headers) {
893
+ for ($i = 0, $c = count($headers); $i < $c; $i++) {
894
+ $header_xml .= $headers[$i]->serialize($this);
895
+ }
896
+ $header_xml = sprintf("<%s:Header>\r\n%s\r\n</%s:Header>\r\n",
897
+ SOAP_BASE::SOAPENVPrefix(), $header_xml,
898
+ SOAP_BASE::SOAPENVPrefix());
899
+ }
900
+
901
+ if (!isset($options['input']) || $options['input'] == 'parse') {
902
+ if (is_array($method)) {
903
+ for ($i = 0, $c = count($method); $i < $c; $i++) {
904
+ $smsg .= $method[$i]->serialize($this);
905
+ }
906
+ } else {
907
+ $smsg = $method->serialize($this);
908
+ }
909
+ } else {
910
+ $smsg = $method;
911
+ }
912
+ $body = sprintf("<%s:Body>%s\r\n</%s:Body>\r\n",
913
+ SOAP_BASE::SOAPENVPrefix(), $smsg,
914
+ SOAP_BASE::SOAPENVPrefix());
915
+
916
+ foreach ($this->_namespaces as $k => $v) {
917
+ $ns_string .= "\r\n " . sprintf('xmlns:%s="%s"', $v, $k);
918
+ }
919
+ if ($this->_namespace) {
920
+ $ns_string .= "\r\n " . sprintf('xmlns="%s"', $this->_namespace);
921
+ }
922
+
923
+ /* If 'use' == 'literal', do not put in the encodingStyle. This is
924
+ * denoted by $this->_section5 being false. 'use' can be defined at a
925
+ * more granular level than we are dealing with here, so this does not
926
+ * work for all services. */
927
+ $xml = sprintf('<?xml version="1.0" encoding="%s"?>%s<%s:Envelope%s',
928
+ $encoding, "\r\n", SOAP_BASE::SOAPENVPrefix(),
929
+ $ns_string);
930
+ if ($this->_section5) {
931
+ $xml .= "\r\n " . sprintf('%s:encodingStyle="%s"',
932
+ SOAP_BASE::SOAPENVPrefix(),
933
+ SOAP_SCHEMA_ENCODING);
934
+ }
935
+ $xml .= sprintf('>%s%s%s</%s:Envelope>' . "\r\n",
936
+ "\r\n", $header_xml, $body, SOAP_BASE::SOAPENVPrefix());
937
+
938
+ return $xml;
939
+ }
940
+
941
+ function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING)
942
+ {
943
+ if (!@include_once 'Mail/mimePart.php') {
944
+ return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
945
+ }
946
+
947
+ // Encode any attachments. See http://www.w3.org/TR/SOAP-attachments
948
+ // Now we have to mime encode the message.
949
+ $params = array('content_type' => 'multipart/related; type="text/xml"');
950
+ $msg = new Mail_mimePart('', $params);
951
+
952
+ // Add the xml part.
953
+ $params['content_type'] = 'text/xml';
954
+ $params['charset'] = $encoding;
955
+ $msg->addSubPart($xml, $params);
956
+
957
+ // Add the attachements
958
+ for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) {
959
+ $msg->addSubPart($this->_attachments[$i]['body'],
960
+ $this->_attachments[$i]);
961
+ }
962
+
963
+ return $msg->encode();
964
+ }
965
+
966
+ // TODO: this needs to be used from the Transport system.
967
+ function _makeDIMEMessage($xml)
968
+ {
969
+ if (!@include_once 'Net/DIME.php') {
970
+ return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
971
+ }
972
+
973
+ // Encode any attachments. See
974
+ // http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
975
+ // Now we have to DIME encode the message
976
+ $dime = new Net_DIME_Message();
977
+ $msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI);
978
+
979
+ // Add the attachments.
980
+ $c = count($this->_attachments);
981
+ for ($i = 0; $i < $c; $i++) {
982
+ $msg .= $dime->encodeData($this->_attachments[$i]['body'],
983
+ $this->_attachments[$i]['content_type'],
984
+ $this->_attachments[$i]['cid'],
985
+ NET_DIME_TYPE_MEDIA);
986
+ }
987
+ $msg .= $dime->endMessage();
988
+
989
+ return $msg;
990
+ }
991
+
992
+ function _decodeMimeMessage(&$data, &$headers, &$attachments)
993
+ {
994
+ if (!@include_once 'Mail/mimeDecode.php') {
995
+ return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
996
+ }
997
+
998
+ $params['include_bodies'] = true;
999
+ $params['decode_bodies'] = true;
1000
+ $params['decode_headers'] = true;
1001
+
1002
+ // Lame thing to have to do for decoding.
1003
+ $decoder = new Mail_mimeDecode($data);
1004
+ $structure = $decoder->decode($params);
1005
+
1006
+ if (isset($structure->body)) {
1007
+ $data = $structure->body;
1008
+ $headers = $structure->headers;
1009
+
1010
+ return;
1011
+ } elseif (isset($structure->parts)) {
1012
+ $data = $structure->parts[0]->body;
1013
+ $headers = array_merge($structure->headers,
1014
+ $structure->parts[0]->headers);
1015
+ if (count($structure->parts) <= 1) {
1016
+ return;
1017
+ }
1018
+
1019
+ $mime_parts = array_splice($structure->parts, 1);
1020
+ // Prepare the parts for the SOAP parser.
1021
+ for ($i = 0, $c = count($mime_parts); $i < $c; $i++) {
1022
+ $p = $mime_parts[$i];
1023
+ if (isset($p->headers['content-location'])) {
1024
+ // TODO: modify location per SwA note section 3
1025
+ // http://www.w3.org/TR/SOAP-attachments
1026
+ $attachments[$p->headers['content-location']] = $p->body;
1027
+ } else {
1028
+ $cid = 'cid:' . substr($p->headers['content-id'], 1, -1);
1029
+ $attachments[$cid] = $p->body;
1030
+ }
1031
+ }
1032
+
1033
+ return;
1034
+ }
1035
+
1036
+ $this->_raiseSoapFault('Mime parsing error', '', '', 'Server');
1037
+ }
1038
+
1039
+ function _decodeDIMEMessage(&$data, &$headers, &$attachments)
1040
+ {
1041
+ if (!@include_once 'Net/DIME.php') {
1042
+ return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
1043
+ }
1044
+
1045
+ // This SHOULD be moved to the transport layer, e.g. PHP itself should
1046
+ // handle parsing DIME ;)
1047
+ $dime = new Net_DIME_Message();
1048
+ $err = $dime->decodeData($data);
1049
+ if (PEAR::isError($err)) {
1050
+ $this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server');
1051
+ return;
1052
+ }
1053
+ if (strcasecmp($dime->parts[0]['type'], SOAP_ENVELOP) != 0) {
1054
+ $this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!', '', '', 'Server');
1055
+ return;
1056
+ }
1057
+
1058
+ $data = $dime->parts[0]['data'];
1059
+ // Fake it for now.
1060
+ $headers['content-type'] = 'text/xml';
1061
+ $c = count($dime->parts);
1062
+ for ($i = 0; $i < $c; $i++) {
1063
+ $part =& $dime->parts[$i];
1064
+ // We need to handle URI's better.
1065
+ $id = strncmp($part['id'], 'cid:', 4)
1066
+ ? 'cid:' . $part['id']
1067
+ : $part['id'];
1068
+ $attachments[$id] = $part['data'];
1069
+ }
1070
+ }
1071
+
1072
+ /**
1073
+ * Explicitly sets the translation for a specific class.
1074
+ *
1075
+ * Auto translation works for all cases, but opens ANY class in the script
1076
+ * to be used as a data type, and may not be desireable.
1077
+ *
1078
+ * @param string $type A SOAP type.
1079
+ * @param string $class A PHP class name.
1080
+ */
1081
+ function setTypeTranslation($type, $class = null)
1082
+ {
1083
+ $tq = new QName($type);
1084
+ if (!$class) {
1085
+ $class = $tq->name;
1086
+ }
1087
+ $this->_type_translation[$type]=$class;
1088
+ }
1089
+
1090
+ }
1091
+
1092
+ /**
1093
+ * Class used to handle QNAME values in XML.
1094
+ *
1095
+ * @package SOAP
1096
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
1097
+ */
1098
+ class QName
1099
+ {
1100
+ var $name = '';
1101
+ var $ns = '';
1102
+ var $namespace = '';
1103
+
1104
+ function QName($name, $namespace = '')
1105
+ {
1106
+ if ($name && $name[0] == '{') {
1107
+ preg_match('/\{(.*?)\}(.*)/', $name, $m);
1108
+ $this->name = $m[2];
1109
+ $this->namespace = $m[1];
1110
+ } elseif (substr_count($name, ':') == 1) {
1111
+ $s = explode(':', $name);
1112
+ $s = array_reverse($s);
1113
+ $this->name = $s[0];
1114
+ $this->ns = $s[1];
1115
+ $this->namespace = $namespace;
1116
+ } else {
1117
+ $this->name = $name;
1118
+ $this->namespace = $namespace;
1119
+ }
1120
+
1121
+ // A little more magic than should be in a qname.
1122
+ $p = strpos($this->name, '[');
1123
+ if ($p) {
1124
+ // TODO: Need to re-examine this logic later.
1125
+ // Chop off [].
1126
+ $this->arraySize = explode(',', substr($this->name, $p + 1, -$p - 2));
1127
+ $this->arrayInfo = substr($this->name, $p);
1128
+ $this->name = substr($this->name, 0, $p);
1129
+ }
1130
+ }
1131
+
1132
+ function fqn()
1133
+ {
1134
+ if ($this->namespace) {
1135
+ return '{' . $this->namespace . '}' . $this->name;
1136
+ } elseif ($this->ns) {
1137
+ return $this->ns . ':' . $this->name;
1138
+ }
1139
+ return $this->name;
1140
+ }
1141
+
1142
+ }
lib/PEAR/SOAP/Client.php ADDED
@@ -0,0 +1,837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for the SOAP client.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
17
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
18
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
19
+ * @author Jan Schneider <jan@horde.org> Maintenance
20
+ * @copyright 2003-2005 The PHP Group
21
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
22
+ * @link http://pear.php.net/package/SOAP
23
+ */
24
+
25
+ /** SOAP_Value */
26
+ require_once 'SOAP/Value.php';
27
+ require_once 'SOAP/Base.php';
28
+ require_once 'SOAP/Transport.php';
29
+ require_once 'SOAP/WSDL.php';
30
+ require_once 'SOAP/Fault.php';
31
+ require_once 'SOAP/Parser.php';
32
+
33
+ // Arnaud: the following code was taken from DataObject and adapted to suit
34
+
35
+ // this will be horrifically slow!!!!
36
+ // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer
37
+ // these two are BC/FC handlers for call in PHP4/5
38
+
39
+ /**
40
+ * @package SOAP
41
+ */
42
+ if (!class_exists('SOAP_Client_Overload', false)) {
43
+ if (substr(zend_version(), 0, 1) > 1) {
44
+ class SOAP_Client_Overload extends SOAP_Base {
45
+ function __call($method, $args)
46
+ {
47
+ $return = null;
48
+ $this->_call($method, $args, $return);
49
+ return $return;
50
+ }
51
+ }
52
+ } else {
53
+ if (!function_exists('clone')) {
54
+ eval('function clone($t) { return $t; }');
55
+ }
56
+ eval('
57
+ class SOAP_Client_Overload extends SOAP_Base {
58
+ function __call($method, $args, &$return)
59
+ {
60
+ return $this->_call($method, $args, $return);
61
+ }
62
+ }');
63
+ }
64
+ }
65
+
66
+ /**
67
+ * SOAP Client Class
68
+ *
69
+ * This class is the main interface for making soap requests.
70
+ *
71
+ * basic usage:<code>
72
+ * $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
73
+ * echo $soapclient->call( string methodname [ , array parameters] );
74
+ * </code>
75
+ * or, if using PHP 5+ or the overload extension:<code>
76
+ * $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
77
+ * echo $soapclient->methodname( [ array parameters] );
78
+ * </code>
79
+ *
80
+ * Originally based on SOAPx4 by Dietrich Ayala
81
+ * http://dietrich.ganx4.com/soapx4
82
+ *
83
+ * @access public
84
+ * @package SOAP
85
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
86
+ * @author Stig Bakken <ssb@fast.no> Conversion to PEAR
87
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
88
+ */
89
+ class SOAP_Client extends SOAP_Client_Overload
90
+ {
91
+ /**
92
+ * Communication endpoint.
93
+ *
94
+ * Currently the following transport formats are supported:
95
+ * - HTTP
96
+ * - SMTP
97
+ *
98
+ * Example endpoints:
99
+ * http://www.example.com/soap/server.php
100
+ * https://www.example.com/soap/server.php
101
+ * mailto:soap@example.com
102
+ *
103
+ * @see SOAP_Client()
104
+ * @var string
105
+ */
106
+ var $_endpoint = '';
107
+
108
+ /**
109
+ * The SOAP PORT name that is used by the client.
110
+ *
111
+ * @var string
112
+ */
113
+ var $_portName = '';
114
+
115
+ /**
116
+ * Endpoint type e.g. 'wdsl'.
117
+ *
118
+ * @var string
119
+ */
120
+ var $_endpointType = '';
121
+
122
+ /**
123
+ * The received xml.
124
+ *
125
+ * @var string
126
+ */
127
+ var $xml;
128
+
129
+ /**
130
+ * The outgoing and incoming data stream for debugging.
131
+ *
132
+ * @var string
133
+ */
134
+ var $wire;
135
+
136
+ /**
137
+ * The outgoing data stream for debugging.
138
+ *
139
+ * @var string
140
+ */
141
+ var $_last_request = null;
142
+
143
+ /**
144
+ * The incoming data stream for debugging.
145
+ *
146
+ * @var string
147
+ */
148
+ var $_last_response = null;
149
+
150
+ /**
151
+ * Options.
152
+ *
153
+ * @var array
154
+ */
155
+ var $_options = array('trace' => false);
156
+
157
+ /**
158
+ * The character encoding used for XML parser, etc.
159
+ *
160
+ * @var string
161
+ */
162
+ var $_encoding = SOAP_DEFAULT_ENCODING;
163
+
164
+ /**
165
+ * The array of SOAP_Headers that we are sending.
166
+ *
167
+ * @var array
168
+ */
169
+ var $headersOut = null;
170
+
171
+ /**
172
+ * The headers we recieved back in the response.
173
+ *
174
+ * @var array
175
+ */
176
+ var $headersIn = null;
177
+
178
+ /**
179
+ * Options for the HTTP_Request class (see HTTP/Request.php).
180
+ *
181
+ * @var array
182
+ */
183
+ var $_proxy_params = array();
184
+
185
+ /**
186
+ * The SOAP_Transport instance.
187
+ *
188
+ * @var SOAP_Transport
189
+ */
190
+ var $_soap_transport = null;
191
+
192
+ /**
193
+ * Constructor.
194
+ *
195
+ * @access public
196
+ *
197
+ * @param string $endpoint An URL.
198
+ * @param boolean $wsdl Whether the endpoint is a WSDL file.
199
+ * @param string $portName The service's port name to use.
200
+ * @param array $proxy_params Options for the HTTP_Request class
201
+ * @see HTTP_Request
202
+ * @param boolean|string $cache Use WSDL caching? The cache directory if
203
+ * a string.
204
+ */
205
+ function SOAP_Client($endpoint, $wsdl = false, $portName = false,
206
+ $proxy_params = array(), $cache = false)
207
+ {
208
+ parent::SOAP_Base('Client');
209
+
210
+ $this->_endpoint = $endpoint;
211
+ $this->_portName = $portName;
212
+ $this->_proxy_params = $proxy_params;
213
+
214
+ // This hack should perhaps be removed as it might cause unexpected
215
+ // behaviour.
216
+ $wsdl = $wsdl
217
+ ? $wsdl
218
+ : strtolower(substr($endpoint, -4)) == 'wsdl';
219
+
220
+ // make values
221
+ if ($wsdl) {
222
+ $this->_endpointType = 'wsdl';
223
+ // instantiate wsdl class
224
+ $this->_wsdl = new SOAP_WSDL($this->_endpoint,
225
+ $this->_proxy_params,
226
+ $cache);
227
+ if ($this->_wsdl->fault) {
228
+ $this->_raiseSoapFault($this->_wsdl->fault);
229
+ }
230
+ }
231
+ }
232
+
233
+ function _reset()
234
+ {
235
+ $this->xml = null;
236
+ $this->wire = null;
237
+ $this->_last_request = null;
238
+ $this->_last_response = null;
239
+ $this->headersIn = null;
240
+ $this->headersOut = null;
241
+ }
242
+
243
+ /**
244
+ * Sets the character encoding.
245
+ *
246
+ * Limited to 'UTF-8', 'US_ASCII' and 'ISO-8859-1'.
247
+ *
248
+ * @access public
249
+ *
250
+ * @param string encoding
251
+ *
252
+ * @return mixed SOAP_Fault on error.
253
+ */
254
+ function setEncoding($encoding)
255
+ {
256
+ if (in_array($encoding, $this->_encodings)) {
257
+ $this->_encoding = $encoding;
258
+ return;
259
+ }
260
+ return $this->_raiseSoapFault('Invalid Encoding');
261
+ }
262
+
263
+ /**
264
+ * Adds a header to the envelope.
265
+ *
266
+ * @access public
267
+ *
268
+ * @param SOAP_Header $soap_value A SOAP_Header or an array with the
269
+ * elements 'name', 'namespace',
270
+ * 'mustunderstand', and 'actor' to send
271
+ * as a header.
272
+ */
273
+ function addHeader($soap_value)
274
+ {
275
+ // Add a new header to the message.
276
+ if (is_a($soap_value, 'SOAP_Header')) {
277
+ $this->headersOut[] = $soap_value;
278
+ } elseif (is_array($soap_value)) {
279
+ // name, value, namespace, mustunderstand, actor
280
+ $this->headersOut[] = new SOAP_Header($soap_value[0],
281
+ null,
282
+ $soap_value[1],
283
+ $soap_value[2],
284
+ $soap_value[3]);
285
+ } else {
286
+ $this->_raiseSoapFault('Invalid parameter provided to addHeader(). Must be an array or a SOAP_Header.');
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Calls a method on the SOAP endpoint.
292
+ *
293
+ * The namespace parameter is overloaded to accept an array of options
294
+ * that can contain data necessary for various transports if it is used as
295
+ * an array, it MAY contain a namespace value and a soapaction value. If
296
+ * it is overloaded, the soapaction parameter is ignored and MUST be
297
+ * placed in the options array. This is done to provide backwards
298
+ * compatibility with current clients, but may be removed in the future.
299
+ * The currently supported values are:
300
+ * - 'namespace'
301
+ * - 'soapaction'
302
+ * - 'timeout': HTTP socket timeout
303
+ * - 'transfer-encoding': SMTP transport, Content-Transfer-Encoding: header
304
+ * - 'from': SMTP transport, From: header
305
+ * - 'subject': SMTP transport, Subject: header
306
+ * - 'headers': SMTP transport, hash of extra SMTP headers
307
+ * - 'attachments': what encoding to use for attachments (Mime, Dime)
308
+ * - 'trace': whether to trace the SOAP communication
309
+ * - 'style': 'document' or 'rpc'; when set to 'document' the parameters
310
+ * are not wrapped inside a tag with the SOAP action name
311
+ * - 'use': 'literal' for literal encoding, anything else for section 5
312
+ * encoding; when set to 'literal' SOAP types will be omitted.
313
+ * - 'keep_arrays_flat': use the tag name multiple times for each element
314
+ * when passing in an array in literal mode
315
+ * - 'no_type_prefix': supress adding of the namespace prefix
316
+ *
317
+ * @access public
318
+ *
319
+ * @param string $method The method to call.
320
+ * @param array $params The method parameters.
321
+ * @param string|array $namespace Namespace or hash with options. Note:
322
+ * most options need to be repeated for
323
+ * SOAP_Value instances.
324
+ * @param string $soapAction
325
+ *
326
+ * @return mixed The method result or a SOAP_Fault on error.
327
+ */
328
+ function call($method, $params, $namespace = false, $soapAction = false)
329
+ {
330
+ $this->headersIn = null;
331
+ $this->_last_request = null;
332
+ $this->_last_response = null;
333
+ $this->wire = null;
334
+ $this->xml = null;
335
+
336
+ $soap_data = $this->_generate($method, $params, $namespace, $soapAction);
337
+ if (PEAR::isError($soap_data)) {
338
+ $fault = $this->_raiseSoapFault($soap_data);
339
+ return $fault;
340
+ }
341
+
342
+ // _generate() may have changed the endpoint if the WSDL has more
343
+ // than one service, so we need to see if we need to generate a new
344
+ // transport to hook to a different URI. Since the transport protocol
345
+ // can also change, we need to get an entirely new object. This could
346
+ // probably be optimized.
347
+ if (!$this->_soap_transport ||
348
+ $this->_endpoint != $this->_soap_transport->url) {
349
+ $this->_soap_transport = SOAP_Transport::getTransport($this->_endpoint);
350
+ if (PEAR::isError($this->_soap_transport)) {
351
+ $fault = $this->_raiseSoapFault($this->_soap_transport);
352
+ $this->_soap_transport = null;
353
+ return $fault;
354
+ }
355
+ }
356
+ $this->_soap_transport->encoding = $this->_encoding;
357
+
358
+ // Send the message.
359
+ $transport_options = array_merge_recursive($this->_proxy_params,
360
+ $this->_options);
361
+ $this->xml = $this->_soap_transport->send($soap_data, $transport_options);
362
+
363
+ // Save the wire information for debugging.
364
+ if ($this->_options['trace']) {
365
+ $this->_last_request = $this->_soap_transport->outgoing_payload;
366
+ $this->_last_response = $this->_soap_transport->incoming_payload;
367
+ $this->wire = $this->getWire();
368
+ }
369
+ if ($this->_soap_transport->fault) {
370
+ $fault = $this->_raiseSoapFault($this->xml);
371
+ return $fault;
372
+ }
373
+
374
+ if (isset($this->_options['result']) &&
375
+ $this->_options['result'] != 'parse') {
376
+ return $this->xml;
377
+ }
378
+
379
+ $this->__result_encoding = $this->_soap_transport->result_encoding;
380
+
381
+ $result = $this->parseResponse($this->xml, $this->__result_encoding,
382
+ $this->_soap_transport->attachments);
383
+ return $result;
384
+ }
385
+
386
+ /**
387
+ * Sets an option to use with the transport layers.
388
+ *
389
+ * For example:
390
+ * <code>
391
+ * $soapclient->setOpt('curl', CURLOPT_VERBOSE, 1)
392
+ * </code>
393
+ * to pass a specific option to curl if using an SSL connection.
394
+ *
395
+ * @access public
396
+ *
397
+ * @param string $category Category to which the option applies or option
398
+ * name.
399
+ * @param string $option An option name if $category is a category name,
400
+ * an option value if $category is an option name.
401
+ * @param string $value An option value if $category is a category
402
+ * name.
403
+ */
404
+ function setOpt($category, $option, $value = null)
405
+ {
406
+ if (!is_null($value)) {
407
+ if (!isset($this->_options[$category])) {
408
+ $this->_options[$category] = array();
409
+ }
410
+ $this->_options[$category][$option] = $value;
411
+ } else {
412
+ $this->_options[$category] = $option;
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Call method supporting the overload extension.
418
+ *
419
+ * If the overload extension is loaded, you can call the client class with
420
+ * a soap method name:
421
+ * <code>
422
+ * $soap = new SOAP_Client(....);
423
+ * $value = $soap->getStockQuote('MSFT');
424
+ * </code>
425
+ *
426
+ * @access public
427
+ *
428
+ * @param string $method The method to call.
429
+ * @param array $params The method parameters.
430
+ * @param mixed $return_value Will get the method's return value
431
+ * assigned.
432
+ *
433
+ * @return boolean Always true.
434
+ */
435
+ function _call($method, $params, &$return_value)
436
+ {
437
+ // Overloading lowercases the method name, we need to look into the
438
+ // WSDL and try to find the correct method name to get the correct
439
+ // case for the call.
440
+ if ($this->_wsdl) {
441
+ $this->_wsdl->matchMethod($method);
442
+ }
443
+
444
+ $return_value = $this->call($method, $params);
445
+
446
+ return true;
447
+ }
448
+
449
+ /**
450
+ * Returns the XML content of the last SOAP request.
451
+ *
452
+ * @return string The last request.
453
+ */
454
+ function getLastRequest()
455
+ {
456
+ return $this->_last_request;
457
+ }
458
+
459
+ /**
460
+ * Returns the XML content of the last SOAP response.
461
+ *
462
+ * @return string The last response.
463
+ */
464
+ function getLastResponse()
465
+ {
466
+ return $this->_last_response;
467
+ }
468
+
469
+ /**
470
+ * Sets the SOAP encoding.
471
+ *
472
+ * The default encoding is section 5 encoded.
473
+ *
474
+ * @param string $use Either 'literal' or 'encoded' (section 5).
475
+ */
476
+ function setUse($use)
477
+ {
478
+ $this->_options['use'] = $use;
479
+ }
480
+
481
+ /**
482
+ * Sets the SOAP encoding style.
483
+ *
484
+ * The default style is rpc.
485
+ *
486
+ * @param string $style Either 'document' or 'rpc'.
487
+ */
488
+ function setStyle($style)
489
+ {
490
+ $this->_options['style'] = $style;
491
+ }
492
+
493
+ /**
494
+ * Sets whether to trace the traffic on the transport level.
495
+ *
496
+ * @see getWire()
497
+ *
498
+ * @param boolean $trace
499
+ */
500
+ function setTrace($trace)
501
+ {
502
+ $this->_options['trace'] = $trace;
503
+ }
504
+
505
+ /**
506
+ * Generates the complete XML SOAP message for an RPC call.
507
+ *
508
+ * @see call()
509
+ *
510
+ * @param string $method The method to call.
511
+ * @param array $params The method parameters.
512
+ * @param string|array $namespace Namespace or hash with options. Note:
513
+ * most options need to be repeated for
514
+ * SOAP_Value instances.
515
+ * @param string $soapAction
516
+ *
517
+ * @return string The SOAP message including envelope.
518
+ */
519
+ function _generate($method, $params, $namespace = false,
520
+ $soapAction = false)
521
+ {
522
+ $this->fault = null;
523
+ $this->_options['input'] = 'parse';
524
+ $this->_options['result'] = 'parse';
525
+ $this->_options['parameters'] = false;
526
+
527
+ if ($params && !is_array($params)) {
528
+ $params = array($params);
529
+ }
530
+
531
+ if (is_array($namespace)) {
532
+ // Options passed as a hash.
533
+ foreach ($namespace as $optname => $opt) {
534
+ $this->_options[strtolower($optname)] = $opt;
535
+ }
536
+ } else {
537
+ // We'll place $soapAction into our array for usage in the
538
+ // transport.
539
+ if ($soapAction) {
540
+ $this->_options['soapaction'] = $soapAction;
541
+ }
542
+ if ($namespace) {
543
+ $this->_options['namespace'] = $namespace;
544
+ }
545
+ }
546
+ if (isset($this->_options['namespace'])) {
547
+ $namespace = $this->_options['namespace'];
548
+ } else {
549
+ $namespace = false;
550
+ }
551
+
552
+ if ($this->_endpointType == 'wsdl') {
553
+ $this->_setSchemaVersion($this->_wsdl->xsd);
554
+
555
+ // Get port name.
556
+ if (!$this->_portName) {
557
+ $this->_portName = $this->_wsdl->getPortName($method);
558
+ }
559
+ if (PEAR::isError($this->_portName)) {
560
+ return $this->_raiseSoapFault($this->_portName);
561
+ }
562
+
563
+ // Get endpoint.
564
+ $this->_endpoint = $this->_wsdl->getEndpoint($this->_portName);
565
+ if (PEAR::isError($this->_endpoint)) {
566
+ return $this->_raiseSoapFault($this->_endpoint);
567
+ }
568
+
569
+ // Get operation data.
570
+ $opData = $this->_wsdl->getOperationData($this->_portName, $method);
571
+
572
+ if (PEAR::isError($opData)) {
573
+ return $this->_raiseSoapFault($opData);
574
+ }
575
+ $namespace = isset($opData['namespace'])?$opData['namespace']:'';
576
+ $this->_options['style'] = $opData['style'];
577
+ $this->_options['use'] = $opData['input']['use'];
578
+ $this->_options['soapaction'] = $opData['soapAction'];
579
+
580
+ // Set input parameters.
581
+ if ($this->_options['input'] == 'parse') {
582
+ $this->_options['parameters'] = $opData['parameters'];
583
+ $nparams = array();
584
+ if (isset($opData['input']['parts']) &&
585
+ count($opData['input']['parts'])) {
586
+ foreach ($opData['input']['parts'] as $name => $part) {
587
+ $xmlns = '';
588
+ $attrs = array();
589
+ // Is the name a complex type?
590
+ if (isset($part['element'])) {
591
+ $xmlns = $this->_wsdl->namespaces[$part['namespace']];
592
+ $part = $this->_wsdl->elements[$part['namespace']][$part['type']];
593
+ $name = $part['name'];
594
+ }
595
+ if (isset($params[$name]) ||
596
+ $this->_wsdl->getDataHandler($name, $part['namespace'])) {
597
+ $nparams[$name] =& $params[$name];
598
+ } else {
599
+ // We now force an associative array for
600
+ // parameters if using WSDL.
601
+ return $this->_raiseSoapFault("The named parameter $name is not in the call parameters.");
602
+ }
603
+ if (gettype($nparams[$name]) != 'object' ||
604
+ !$nparams[$name] instanceof SOAP_Value) {
605
+ // Type is likely a qname, split it apart, and get
606
+ // the type namespace from WSDL.
607
+ $qname = new QName($part['type']);
608
+ if ($qname->ns) {
609
+ $type_namespace = $this->_wsdl->namespaces[$qname->ns];
610
+ } elseif (isset($part['namespace'])) {
611
+ $type_namespace = $this->_wsdl->namespaces[$part['namespace']];
612
+ } else {
613
+ $type_namespace = null;
614
+ }
615
+ $qname->namespace = $type_namespace;
616
+ $pqname = $name;
617
+ if ($xmlns) {
618
+ $pqname = '{' . $xmlns . '}' . $name;
619
+ }
620
+ $nparams[$name] = new SOAP_Value($pqname,
621
+ $qname->fqn(),
622
+ $nparams[$name],
623
+ $attrs);
624
+ } else {
625
+ // WSDL fixups to the SOAP value.
626
+ }
627
+ }
628
+ }
629
+ $params =& $nparams;
630
+ unset($nparams);
631
+ }
632
+ } else {
633
+ $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
634
+ }
635
+
636
+ // Serialize the message.
637
+ $this->_section5 = (!isset($this->_options['use']) ||
638
+ $this->_options['use'] != 'literal');
639
+
640
+ if (!isset($this->_options['style']) ||
641
+ $this->_options['style'] == 'rpc') {
642
+ $this->_options['style'] = 'rpc';
643
+ $this->docparams = true;
644
+ $mqname = new QName($method, $namespace);
645
+ $methodValue = new SOAP_Value($mqname->fqn(), 'Struct', $params,
646
+ array(), $this->_options);
647
+ $soap_msg = $this->makeEnvelope($methodValue,
648
+ $this->headersOut,
649
+ $this->_encoding,
650
+ $this->_options);
651
+ } else {
652
+ if (!$params) {
653
+ $mqname = new QName($method, $namespace);
654
+ $params = new SOAP_Value($mqname->fqn(), 'Struct', null);
655
+ } elseif ($this->_options['input'] == 'parse') {
656
+ if (is_array($params)) {
657
+ $nparams = array();
658
+ $keys = array_keys($params);
659
+ foreach ($keys as $k) {
660
+ if (gettype($params[$k]) != 'object') {
661
+ $nparams[] = new SOAP_Value($k,
662
+ false,
663
+ $params[$k]);
664
+ } else {
665
+ $nparams[] =& $params[$k];
666
+ }
667
+ }
668
+ $params =& $nparams;
669
+ }
670
+ if ($this->_options['parameters']) {
671
+ $mqname = new QName($method, $namespace);
672
+ $params = new SOAP_Value($mqname->fqn(),
673
+ 'Struct',
674
+ $params);
675
+ }
676
+ }
677
+ $soap_msg = $this->makeEnvelope($params,
678
+ $this->headersOut,
679
+ $this->_encoding,
680
+ $this->_options);
681
+ }
682
+ $this->headersOut = null;
683
+
684
+ if (PEAR::isError($soap_msg)) {
685
+ return $this->_raiseSoapFault($soap_msg);
686
+ }
687
+
688
+ // Handle MIME or DIME encoding.
689
+ // TODO: DIME encoding should move to the transport, do it here for
690
+ // now and for ease of getting it done.
691
+ if (count($this->_attachments)) {
692
+ if ((isset($this->_options['attachments']) &&
693
+ $this->_options['attachments'] == 'Mime') ||
694
+ isset($this->_options['Mime'])) {
695
+ $soap_msg = $this->_makeMimeMessage($soap_msg, $this->_encoding);
696
+ } else {
697
+ // default is dime
698
+ $soap_msg = $this->_makeDIMEMessage($soap_msg, $this->_encoding);
699
+ $this->_options['headers']['Content-Type'] = 'application/dime';
700
+ }
701
+ if (PEAR::isError($soap_msg)) {
702
+ return $this->_raiseSoapFault($soap_msg);
703
+ }
704
+ }
705
+
706
+ // Instantiate client.
707
+ if (is_array($soap_msg)) {
708
+ $soap_data = $soap_msg['body'];
709
+ if (count($soap_msg['headers'])) {
710
+ if (isset($this->_options['headers'])) {
711
+ $this->_options['headers'] = array_merge($this->_options['headers'], $soap_msg['headers']);
712
+ } else {
713
+ $this->_options['headers'] = $soap_msg['headers'];
714
+ }
715
+ }
716
+ } else {
717
+ $soap_data = $soap_msg;
718
+ }
719
+
720
+ return $soap_data;
721
+ }
722
+
723
+ /**
724
+ * Parses a SOAP response.
725
+ *
726
+ * @see SOAP_Parser::
727
+ *
728
+ * @param string $response XML content of SOAP response.
729
+ * @param string $encoding Character set encoding, defaults to 'UTF-8'.
730
+ * @param array $attachments List of attachments.
731
+ */
732
+ function parseResponse($response, $encoding, $attachments)
733
+ {
734
+ // Parse the response.
735
+ $response = new SOAP_Parser($response, $encoding, $attachments);
736
+ if ($response->fault) {
737
+ $fault = $this->_raiseSoapFault($response->fault);
738
+ return $fault;
739
+ }
740
+
741
+ // Return array of parameters.
742
+ $return = $response->getResponse();
743
+ $headers = $response->getHeaders();
744
+ if ($headers) {
745
+ $this->headersIn = $this->_decodeResponse($headers, false);
746
+ }
747
+
748
+ $decoded = $this->_decodeResponse($return);
749
+ return $decoded;
750
+ }
751
+
752
+ /**
753
+ * Converts a complex SOAP_Value into a PHP Array
754
+ *
755
+ * @param SOAP_Value $response Value object.
756
+ * @param boolean $shift
757
+ *
758
+ * @return array
759
+ */
760
+ function _decodeResponse($response, $shift = true)
761
+ {
762
+ if (!$response) {
763
+ $decoded = null;
764
+ return $decoded;
765
+ }
766
+
767
+ // Check for valid response.
768
+ if (PEAR::isError($response)) {
769
+ $fault = $this->_raiseSoapFault($response);
770
+ return $fault;
771
+ } elseif (!$response instanceof SOAP_Value) {
772
+ $fault = $this->_raiseSoapFault("Didn't get SOAP_Value object back from client");
773
+ return $fault;
774
+ }
775
+
776
+ // Decode to native php datatype.
777
+ $returnArray = $this->_decode($response);
778
+
779
+ // Fault?
780
+ if (PEAR::isError($returnArray)) {
781
+ $fault = $this->_raiseSoapFault($returnArray);
782
+ return $fault;
783
+ }
784
+
785
+ if (is_object($returnArray) &&
786
+ strcasecmp(get_class($returnArray), 'stdClass') == 0) {
787
+ $returnArray = get_object_vars($returnArray);
788
+ }
789
+
790
+ if (is_array($returnArray)) {
791
+ if (isset($returnArray['faultcode']) ||
792
+ isset($returnArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) {
793
+ $faultcode = $faultstring = $faultdetail = $faultactor = '';
794
+ foreach ($returnArray as $k => $v) {
795
+ if (stristr($k, 'faultcode')) $faultcode = $v;
796
+ if (stristr($k, 'faultstring')) $faultstring = $v;
797
+ if (stristr($k, 'detail')) $faultdetail = $v;
798
+ if (stristr($k, 'faultactor')) $faultactor = $v;
799
+ }
800
+ $fault = $this->_raiseSoapFault($faultstring, $faultdetail,
801
+ $faultactor, $faultcode);
802
+ return $fault;
803
+ }
804
+ // Return array of return values.
805
+ if ($shift && count($returnArray) == 1) {
806
+ $decoded = array_shift($returnArray);
807
+ return $decoded;
808
+ }
809
+ return $returnArray;
810
+ }
811
+
812
+ return $returnArray;
813
+ }
814
+
815
+ /**
816
+ * Returns the outgoing and incoming traffic on the transport level.
817
+ *
818
+ * Tracing has to be enabled.
819
+ *
820
+ * @see setTrace()
821
+ *
822
+ * @return string The complete traffic between the client and the server.
823
+ */
824
+ function getWire()
825
+ {
826
+ if ($this->_options['trace'] &&
827
+ ($this->_last_request || $this->_last_response)) {
828
+ return "OUTGOING:\n\n" .
829
+ $this->_last_request .
830
+ "\n\nINCOMING\n\n" .
831
+ preg_replace("/></",">\r\n<", $this->_last_response);
832
+ }
833
+
834
+ return null;
835
+ }
836
+
837
+ }
lib/PEAR/SOAP/Fault.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the SOAP_Fault class, used for all error objects in this
4
+ * package.
5
+ *
6
+ * PHP versions 4 and 5
7
+ *
8
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
9
+ * that is bundled with this package in the file LICENSE, and is available at
10
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
11
+ * did not receive a copy of the PHP license and are unable to obtain it
12
+ * through the world-wide-web, please send a note to license@php.net so we can
13
+ * mail you a copy immediately.
14
+ *
15
+ * @category Web Services
16
+ * @package SOAP
17
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
18
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
19
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
20
+ * @author Jan Schneider <jan@horde.org> Maintenance
21
+ * @copyright 2003-2006 The PHP Group
22
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
23
+ * @link http://pear.php.net/package/SOAP
24
+ */
25
+
26
+ /** PEAR_Error */
27
+ require_once 'PEAR.php';
28
+
29
+ /**
30
+ * PEAR::Error wrapper used to match SOAP Faults to PEAR Errors
31
+ *
32
+ * SOAP_Fault can provide a complete backtrace of the error. Revealing these
33
+ * details in a public web services is a bad idea because it can be used by
34
+ * attackers. Thus you have to enable backtrace information in SOAP_Fault
35
+ * responses by putting the following code in your script after your
36
+ * "require_once 'SOAP/Server.php';" line:
37
+ *
38
+ * <code>
39
+ * $backtrace =& PEAR::getStaticProperty('SOAP_Fault', 'backtrace');
40
+ * $backtrace = true;
41
+ * </code>
42
+ *
43
+ * @package SOAP
44
+ * @access public
45
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
46
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
47
+ */
48
+ class SOAP_Fault extends PEAR_Error
49
+ {
50
+ /**
51
+ * Constructor.
52
+ *
53
+ * @param string $faultstring Message string for fault.
54
+ * @param mixed $faultcode The faultcode.
55
+ * @param mixed $faultactor
56
+ * @param mixed $detail @see PEAR_Error
57
+ * @param array $mode @see PEAR_Error
58
+ * @param array $options @see PEAR_Error
59
+ */
60
+ function SOAP_Fault($faultstring = 'unknown error', $faultcode = 'Client',
61
+ $faultactor = null, $detail = null, $mode = null,
62
+ $options = null)
63
+ {
64
+ parent::PEAR_Error($faultstring, $faultcode, $mode, $options, $detail);
65
+ if ($faultactor) {
66
+ $this->error_message_prefix = $faultactor;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Returns a SOAP XML message that can be sent as a server response.
72
+ *
73
+ * @return string
74
+ */
75
+ function message($encoding = SOAP_DEFAULT_ENCODING)
76
+ {
77
+ $msg = new SOAP_Base();
78
+ $params = array();
79
+ $params[] = new SOAP_Value('faultcode', 'QName', SOAP_BASE::SOAPENVPrefix().':' . $this->code);
80
+ $params[] = new SOAP_Value('faultstring', 'string', $this->message);
81
+ $params[] = new SOAP_Value('faultactor', 'anyURI', $this->error_message_prefix);
82
+ if (PEAR::getStaticProperty('SOAP_Fault', 'backtrace') &&
83
+ isset($this->backtrace)) {
84
+ $params[] = new SOAP_Value('detail', 'string', $this->backtrace);
85
+ } else {
86
+ $params[] = new SOAP_Value('detail', 'string', $this->userinfo);
87
+ }
88
+
89
+ $methodValue = new SOAP_Value('{' . SOAP_ENVELOP . '}Fault', 'Struct', $params);
90
+ $headers = null;
91
+ return $msg->makeEnvelope($methodValue, $headers, $encoding);
92
+ }
93
+
94
+ /**
95
+ * Returns a simple native PHP array containing the fault data.
96
+ *
97
+ * @return array
98
+ */
99
+ function getFault()
100
+ {
101
+ $fault = new stdClass();
102
+ $fault->faultcode = $this->code;
103
+ $fault->faultstring = $this->message;
104
+ $fault->faultactor = $this->error_message_prefix;
105
+ $fault->detail = $this->userinfo;
106
+ return $fault;
107
+ }
108
+
109
+ /**
110
+ * Returns the SOAP actor for the fault.
111
+ *
112
+ * @return string
113
+ */
114
+ function getActor()
115
+ {
116
+ return $this->error_message_prefix;
117
+ }
118
+
119
+ /**
120
+ * Returns the fault detail.
121
+ *
122
+ * @return string
123
+ */
124
+ function getDetail()
125
+ {
126
+ return $this->userinfo;
127
+ }
128
+
129
+ }
lib/PEAR/SOAP/Parser.php ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for the SOAP message parser.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
17
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
18
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
19
+ * @author Jan Schneider <jan@horde.org> Maintenance
20
+ * @copyright 2003-2005 The PHP Group
21
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
22
+ * @link http://pear.php.net/package/SOAP
23
+ */
24
+
25
+ require_once 'SOAP/Base.php';
26
+ require_once 'SOAP/Value.php';
27
+
28
+ /**
29
+ * SOAP Parser
30
+ *
31
+ * This class is used by SOAP::Message and SOAP::Server to parse soap
32
+ * packets. Originally based on SOAPx4 by Dietrich Ayala
33
+ * http://dietrich.ganx4.com/soapx4
34
+ *
35
+ * @access public
36
+ * @package SOAP
37
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
38
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
39
+ */
40
+ class SOAP_Parser extends SOAP_Base
41
+ {
42
+ var $status = '';
43
+ var $position = 0;
44
+ var $depth = 0;
45
+ var $default_namespace = '';
46
+ var $message = array();
47
+ var $depth_array = array();
48
+ var $parent = 0;
49
+ var $root_struct_name = array();
50
+ var $header_struct_name = array();
51
+ var $curent_root_struct_name = '';
52
+ var $root_struct = array();
53
+ var $header_struct = array();
54
+ var $curent_root_struct = 0;
55
+ var $references = array();
56
+ var $need_references = array();
57
+
58
+ /**
59
+ * Used to handle non-root elements before root body element.
60
+ *
61
+ * @var integer
62
+ */
63
+ var $bodyDepth;
64
+
65
+ /**
66
+ * Constructor.
67
+ *
68
+ * @param string $xml XML content.
69
+ * @param string $encoding Character set encoding, defaults to 'UTF-8'.
70
+ * @param array $attachments List of attachments.
71
+ */
72
+ function SOAP_Parser($xml, $encoding = SOAP_DEFAULT_ENCODING,
73
+ $attachments = null)
74
+ {
75
+ parent::SOAP_Base('Parser');
76
+ $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION);
77
+
78
+ $this->attachments = $attachments;
79
+
80
+ // Check the XML tag for encoding.
81
+ if (preg_match('/<\?xml[^>]+encoding\s*?=\s*?(\'([^\']*)\'|"([^"]*)")[^>]*?[\?]>/', $xml, $m)) {
82
+ $encoding = strtoupper($m[2] ? $m[2] : $m[3]);
83
+ }
84
+
85
+ // Determine where in the message we are (envelope, header, body,
86
+ // method). Check whether content has been read.
87
+ if (!empty($xml)) {
88
+ // Prepare the XML parser.
89
+ $parser = xml_parser_create($encoding);
90
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
91
+ xml_set_object($parser, $this);
92
+ xml_set_element_handler($parser, '_startElement', '_endElement');
93
+ xml_set_character_data_handler($parser, '_characterData');
94
+
95
+ // Some lame SOAP implementations add nul bytes at the end of the
96
+ // SOAP stream, and expat chokes on that.
97
+ if ($xml[strlen($xml) - 1] == 0) {
98
+ $xml = trim($xml);
99
+ }
100
+
101
+ // Parse the XML file.
102
+ if (!xml_parse($parser, $xml, true)) {
103
+ $err = sprintf('XML error on line %d col %d byte %d %s',
104
+ xml_get_current_line_number($parser),
105
+ xml_get_current_column_number($parser),
106
+ xml_get_current_byte_index($parser),
107
+ xml_error_string(xml_get_error_code($parser)));
108
+ $this->_raiseSoapFault($err, htmlspecialchars($xml));
109
+ }
110
+ xml_parser_free($parser);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Returns an array of responses.
116
+ *
117
+ * After parsing a SOAP message, use this to get the response.
118
+ *
119
+ * @return array
120
+ */
121
+ function getResponse()
122
+ {
123
+ if (!empty($this->root_struct[0])) {
124
+ return $this->_buildResponse($this->root_struct[0]);
125
+ } else {
126
+ return $this->_raiseSoapFault('Cannot build response');
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Returns an array of header responses.
132
+ *
133
+ * After parsing a SOAP message, use this to get the response.
134
+ *
135
+ * @return array
136
+ */
137
+ function getHeaders()
138
+ {
139
+ if (!empty($this->header_struct[0])) {
140
+ return $this->_buildResponse($this->header_struct[0]);
141
+ } else {
142
+ // We don't fault if there are no headers; that can be handled by
143
+ // the application if necessary.
144
+ return null;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Recurses to build a multi dimensional array.
150
+ *
151
+ * @see _buildResponse()
152
+ */
153
+ function _domulti($d, &$ar, &$r, &$v, $ad = 0)
154
+ {
155
+ if ($d) {
156
+ $this->_domulti($d - 1, $ar, $r[$ar[$ad]], $v, $ad + 1);
157
+ } else {
158
+ $r = $v;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Loops through the message, building response structures.
164
+ *
165
+ * @param integer $pos Position.
166
+ *
167
+ * @return SOAP_Value
168
+ */
169
+ function _buildResponse($pos)
170
+ {
171
+ $response = null;
172
+
173
+ if (isset($this->message[$pos]['children'])) {
174
+ $children = explode('|', $this->message[$pos]['children']);
175
+ foreach ($children as $c => $child_pos) {
176
+ if ($this->message[$child_pos]['type'] != null) {
177
+ $response[] = $this->_buildResponse($child_pos);
178
+ }
179
+ }
180
+ if (isset($this->message[$pos]['arraySize'])) {
181
+ $ardepth = count($this->message[$pos]['arraySize']);
182
+ if ($ardepth > 1) {
183
+ $ar = array_pad(array(), $ardepth, 0);
184
+ if (isset($this->message[$pos]['arrayOffset'])) {
185
+ for ($i = 0; $i < $ardepth; $i++) {
186
+ $ar[$i] += $this->message[$pos]['arrayOffset'][$i];
187
+ }
188
+ }
189
+ $elc = count($response);
190
+ for ($i = 0; $i < $elc; $i++) {
191
+ // Recurse to build a multi dimensional array.
192
+ $this->_domulti($ardepth, $ar, $newresp, $response[$i]);
193
+
194
+ // Increment our array pointers.
195
+ $ad = $ardepth - 1;
196
+ $ar[$ad]++;
197
+ while ($ad > 0 &&
198
+ $ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) {
199
+ $ar[$ad] = 0;
200
+ $ad--;
201
+ $ar[$ad]++;
202
+ }
203
+ }
204
+ $response = $newresp;
205
+ } elseif (isset($this->message[$pos]['arrayOffset']) &&
206
+ $this->message[$pos]['arrayOffset'][0] > 0) {
207
+ // Check for padding.
208
+ $pad = $this->message[$pos]['arrayOffset'][0] + count($response) * -1;
209
+ $response = array_pad($response, $pad, null);
210
+ }
211
+ }
212
+ }
213
+
214
+ // Build attributes.
215
+ $attrs = array();
216
+ foreach ($this->message[$pos]['attrs'] as $atn => $atv) {
217
+ if (!strstr($atn, 'xmlns') && !strpos($atn, ':')) {
218
+ $attrs[$atn] = $atv;
219
+ }
220
+ }
221
+
222
+ // Add current node's value.
223
+ $nqn = new QName($this->message[$pos]['name'],
224
+ $this->message[$pos]['namespace']);
225
+ $tqn = new QName($this->message[$pos]['type'],
226
+ $this->message[$pos]['type_namespace']);
227
+ if ($response) {
228
+ $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), $response,
229
+ $attrs);
230
+ if (isset($this->message[$pos]['arrayType'])) {
231
+ $response->arrayType = $this->message[$pos]['arrayType'];
232
+ }
233
+ } else {
234
+ // Check if value is an empty array
235
+ if ($tqn->name == 'Array') {
236
+ $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), array(),
237
+ $attrs);
238
+ //if ($pos == 4) var_dump($this->message[$pos], $response);
239
+ } else {
240
+ $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(),
241
+ $this->message[$pos]['cdata'],
242
+ $attrs);
243
+ }
244
+ }
245
+
246
+ // Handle header attribute that we need.
247
+ if (array_key_exists('actor', $this->message[$pos])) {
248
+ $response->actor = $this->message[$pos]['actor'];
249
+ }
250
+ if (array_key_exists('mustUnderstand', $this->message[$pos])) {
251
+ $response->mustunderstand = $this->message[$pos]['mustUnderstand'];
252
+ }
253
+
254
+ return $response;
255
+ }
256
+
257
+ /**
258
+ * Start element handler used with the XML parser.
259
+ */
260
+ function _startElement($parser, $name, $attrs)
261
+ {
262
+ // Position in a total number of elements, starting from 0.
263
+ // Update class level position.
264
+ $pos = $this->position++;
265
+
266
+ // And set mine.
267
+ $this->message[$pos] = array(
268
+ 'type' => '',
269
+ 'type_namespace' => '',
270
+ 'cdata' => '',
271
+ 'pos' => $pos,
272
+ 'id' => '');
273
+
274
+ // Parent/child/depth determinations.
275
+
276
+ // depth = How many levels removed from root?
277
+ // Set mine as current global depth and increment global depth value.
278
+ $this->message[$pos]['depth'] = $this->depth++;
279
+
280
+ // Else add self as child to whoever the current parent is.
281
+ if ($pos != 0) {
282
+ if (isset($this->message[$this->parent]['children'])) {
283
+ $this->message[$this->parent]['children'] .= '|' . $pos;
284
+ } else {
285
+ $this->message[$this->parent]['children'] = $pos;
286
+ }
287
+ }
288
+
289
+ // Set my parent.
290
+ $this->message[$pos]['parent'] = $this->parent;
291
+
292
+ // Set self as current value for this depth.
293
+ $this->depth_array[$this->depth] = $pos;
294
+ // Set self as current parent.
295
+ $this->parent = $pos;
296
+ $qname = new QName($name);
297
+ // Set status.
298
+ if (strcasecmp('envelope', $qname->name) == 0) {
299
+ $this->status = 'envelope';
300
+ } elseif (strcasecmp('header', $qname->name) == 0) {
301
+ $this->status = 'header';
302
+ $this->header_struct_name[] = $this->curent_root_struct_name = $qname->name;
303
+ $this->header_struct[] = $this->curent_root_struct = $pos;
304
+ $this->message[$pos]['type'] = 'Struct';
305
+ } elseif (strcasecmp('body', $qname->name) == 0) {
306
+ $this->status = 'body';
307
+ $this->bodyDepth = $this->depth;
308
+
309
+ // Set method
310
+ } elseif ($this->status == 'body') {
311
+ // Is this element allowed to be a root?
312
+ // TODO: this needs to be optimized, we loop through $attrs twice
313
+ // now.
314
+ $can_root = $this->depth == $this->bodyDepth + 1;
315
+ if ($can_root) {
316
+ foreach ($attrs as $key => $value) {
317
+ if (stristr($key, ':root') && !$value) {
318
+ $can_root = false;
319
+ }
320
+ }
321
+ }
322
+
323
+ if ($can_root) {
324
+ $this->status = 'method';
325
+ $this->root_struct_name[] = $this->curent_root_struct_name = $qname->name;
326
+ $this->root_struct[] = $this->curent_root_struct = $pos;
327
+ $this->message[$pos]['type'] = 'Struct';
328
+ }
329
+ }
330
+
331
+ // Set my status.
332
+ $this->message[$pos]['status'] = $this->status;
333
+
334
+ // Set name.
335
+ $this->message[$pos]['name'] = htmlspecialchars($qname->name);
336
+
337
+ // Set attributes.
338
+ $this->message[$pos]['attrs'] = $attrs;
339
+
340
+ // Loop through attributes, logging ns and type declarations.
341
+ foreach ($attrs as $key => $value) {
342
+ // If ns declarations, add to class level array of valid
343
+ // namespaces.
344
+ $kqn = new QName($key);
345
+ if ($kqn->ns == 'xmlns') {
346
+ $prefix = $kqn->name;
347
+
348
+ if (in_array($value, $this->_XMLSchema)) {
349
+ $this->_setSchemaVersion($value);
350
+ }
351
+
352
+ $this->_namespaces[$value] = $prefix;
353
+
354
+ // Set method namespace.
355
+ } elseif ($key == 'xmlns') {
356
+ $qname->ns = $this->_getNamespacePrefix($value);
357
+ $qname->namespace = $value;
358
+ } elseif ($kqn->name == 'actor') {
359
+ $this->message[$pos]['actor'] = $value;
360
+ } elseif ($kqn->name == 'mustUnderstand') {
361
+ $this->message[$pos]['mustUnderstand'] = $value;
362
+
363
+ // If it's a type declaration, set type.
364
+ } elseif ($kqn->name == 'type') {
365
+ $vqn = new QName($value);
366
+ $this->message[$pos]['type'] = $vqn->name;
367
+ $this->message[$pos]['type_namespace'] = $this->_getNamespaceForPrefix($vqn->ns);
368
+
369
+ // Should do something here with the namespace of specified
370
+ // type?
371
+
372
+ } elseif ($kqn->name == 'arrayType') {
373
+ $vqn = new QName($value);
374
+ $this->message[$pos]['type'] = 'Array';
375
+ if (isset($vqn->arraySize)) {
376
+ $this->message[$pos]['arraySize'] = $vqn->arraySize;
377
+ }
378
+ $this->message[$pos]['arrayType'] = $vqn->name;
379
+
380
+ } elseif ($kqn->name == 'offset') {
381
+ $this->message[$pos]['arrayOffset'] = split(',', substr($value, 1, strlen($value) - 2));
382
+
383
+ } elseif ($kqn->name == 'id') {
384
+ // Save id to reference array.
385
+ $this->references[$value] = $pos;
386
+ $this->message[$pos]['id'] = $value;
387
+
388
+ } elseif ($kqn->name == 'href') {
389
+ if ($value[0] == '#') {
390
+ $ref = substr($value, 1);
391
+ if (isset($this->references[$ref])) {
392
+ // cdata, type, inval.
393
+ $ref_pos = $this->references[$ref];
394
+ $this->message[$pos]['children'] = &$this->message[$ref_pos]['children'];
395
+ $this->message[$pos]['cdata'] = &$this->message[$ref_pos]['cdata'];
396
+ $this->message[$pos]['type'] = &$this->message[$ref_pos]['type'];
397
+ $this->message[$pos]['arraySize'] = &$this->message[$ref_pos]['arraySize'];
398
+ $this->message[$pos]['arrayType'] = &$this->message[$ref_pos]['arrayType'];
399
+ } else {
400
+ // Reverse reference, store in 'need reference'.
401
+ if (!isset($this->need_references[$ref])) {
402
+ $this->need_references[$ref] = array();
403
+ }
404
+ $this->need_references[$ref][] = $pos;
405
+ }
406
+ } elseif (isset($this->attachments[$value])) {
407
+ $this->message[$pos]['cdata'] = $this->attachments[$value];
408
+ }
409
+ }
410
+ }
411
+ // See if namespace is defined in tag.
412
+ if (isset($attrs['xmlns:' . $qname->ns])) {
413
+ $namespace = $attrs['xmlns:' . $qname->ns];
414
+ } elseif ($qname->ns && !$qname->namespace) {
415
+ $namespace = $this->_getNamespaceForPrefix($qname->ns);
416
+ } else {
417
+ // Get namespace.
418
+ $namespace = $qname->namespace ? $qname->namespace : $this->default_namespace;
419
+ }
420
+ $this->message[$pos]['namespace'] = $namespace;
421
+ $this->default_namespace = $namespace;
422
+ }
423
+
424
+ /**
425
+ * End element handler used with the XML parser.
426
+ */
427
+ function _endElement($parser, $name)
428
+ {
429
+ // Position of current element is equal to the last value left in
430
+ // depth_array for my depth.
431
+ $pos = $this->depth_array[$this->depth];
432
+
433
+ // Bring depth down a notch.
434
+ $this->depth--;
435
+ $qname = new QName($name);
436
+
437
+ // Get type if not explicitly declared in an xsi:type attribute.
438
+ // TODO: check on integrating WSDL validation here.
439
+ if ($this->message[$pos]['type'] == '') {
440
+ if (isset($this->message[$pos]['children'])) {
441
+ /* this is slow, need to look at some faster method
442
+ $children = explode('|', $this->message[$pos]['children']);
443
+ if (count($children) > 2 &&
444
+ $this->message[$children[1]]['name'] == $this->message[$children[2]]['name']) {
445
+ $this->message[$pos]['type'] = 'Array';
446
+ } else {
447
+ $this->message[$pos]['type'] = 'Struct';
448
+ }*/
449
+ $this->message[$pos]['type'] = 'Struct';
450
+ } else {
451
+ $parent = $this->message[$pos]['parent'];
452
+ if ($this->message[$parent]['type'] == 'Array' &&
453
+ isset($this->message[$parent]['arrayType'])) {
454
+ $this->message[$pos]['type'] = $this->message[$parent]['arrayType'];
455
+ } else {
456
+ $this->message[$pos]['type'] = 'string';
457
+ }
458
+ }
459
+ }
460
+
461
+ // If tag we are currently closing is the method wrapper.
462
+ if ($pos == $this->curent_root_struct) {
463
+ $this->status = 'body';
464
+ } elseif ($qname->name == 'Body' || $qname->name == 'Header') {
465
+ $this->status = 'envelope';
466
+ }
467
+
468
+ // Set parent back to my parent.
469
+ $this->parent = $this->message[$pos]['parent'];
470
+
471
+ // Handle any reverse references now.
472
+ $idref = $this->message[$pos]['id'];
473
+
474
+ if ($idref != '' && isset($this->need_references[$idref])) {
475
+ foreach ($this->need_references[$idref] as $ref_pos) {
476
+ // XXX is this stuff there already?
477
+ $this->message[$ref_pos]['children'] = &$this->message[$pos]['children'];
478
+ $this->message[$ref_pos]['cdata'] = &$this->message[$pos]['cdata'];
479
+ $this->message[$ref_pos]['type'] = &$this->message[$pos]['type'];
480
+ $this->message[$ref_pos]['arraySize'] = &$this->message[$pos]['arraySize'];
481
+ $this->message[$ref_pos]['arrayType'] = &$this->message[$pos]['arrayType'];
482
+ }
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Element content handler used with the XML parser.
488
+ */
489
+ function _characterData($parser, $data)
490
+ {
491
+ $pos = $this->depth_array[$this->depth];
492
+ if (isset($this->message[$pos]['cdata'])) {
493
+ $this->message[$pos]['cdata'] .= $data;
494
+ } else {
495
+ $this->message[$pos]['cdata'] = $data;
496
+ }
497
+ }
498
+
499
+ }
lib/PEAR/SOAP/Transport.php ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for an abstract transport layer.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Dietrich Ayala <dietrich@ganx4.com>
17
+ * @author Shane Caraveo <Shane@Caraveo.com>
18
+ * @author Jan Schneider <jan@horde.org>
19
+ * @copyright 2003-2006 The PHP Group
20
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
21
+ * @link http://pear.php.net/package/SOAP
22
+ */
23
+
24
+ require_once 'SOAP/Base.php';
25
+
26
+ /**
27
+ * SOAP Transport Layer
28
+ *
29
+ * This layer can use different protocols dependant on the endpoint url
30
+ * provided.
31
+ *
32
+ * No knowlege of the SOAP protocol is available at this level.
33
+ * No knowlege of the transport protocols is available at this level.
34
+ *
35
+ * @access public
36
+ * @package SOAP
37
+ * @author Shane Caraveo <shane@php.net>
38
+ * @author Jan Schneider <jan@horde.org>
39
+ */
40
+ class SOAP_Transport extends SOAP_Base
41
+ {
42
+ /**
43
+ * Connection endpoint URL.
44
+ *
45
+ * @var string
46
+ */
47
+ var $url = '';
48
+
49
+ /**
50
+ * Array containing urlparts.
51
+ *
52
+ * @see parse_url()
53
+ *
54
+ * @var mixed
55
+ */
56
+ var $urlparts = null;
57
+
58
+ /**
59
+ * Incoming payload.
60
+ *
61
+ * @var string
62
+ */
63
+ var $incoming_payload = '';
64
+
65
+ /**
66
+ * Outgoing payload.
67
+ *
68
+ * @var string
69
+ */
70
+ var $outgoing_payload = '';
71
+
72
+ /**
73
+ * Request encoding.
74
+ *
75
+ * @var string
76
+ */
77
+ var $encoding = SOAP_DEFAULT_ENCODING;
78
+
79
+ /**
80
+ * Response encoding.
81
+ *
82
+ * We assume UTF-8 if no encoding is set.
83
+ *
84
+ * @var string
85
+ */
86
+ var $result_encoding = 'UTF-8';
87
+
88
+ /**
89
+ * Decoded attachments from the reponse.
90
+ *
91
+ * @var array
92
+ */
93
+ var $attachments;
94
+
95
+ /**
96
+ * Request User-Agent.
97
+ *
98
+ * @var string
99
+ */
100
+ var $_userAgent = SOAP_LIBRARY_NAME;
101
+
102
+ /**
103
+ * Sends and receives SOAP data.
104
+ *
105
+ * @access public
106
+ * @abstract
107
+ *
108
+ * @param string Outgoing SOAP data.
109
+ * @param array Options.
110
+ *
111
+ * @return string|SOAP_Fault
112
+ */
113
+ function send($msg, $options = null)
114
+ {
115
+ return $this->_raiseSoapFault('SOAP_Transport::send() not implemented.');
116
+ }
117
+
118
+ public static function getTransport($url, $encoding = SOAP_DEFAULT_ENCODING)
119
+ {
120
+ $urlparts = @parse_url($url);
121
+
122
+ if (!$urlparts['scheme']) {
123
+ return SOAP_Base_Object::_raiseSoapFault("Invalid transport URI: $url");
124
+ }
125
+
126
+ if (strcasecmp($urlparts['scheme'], 'mailto') == 0) {
127
+ $transport_type = 'SMTP';
128
+ } elseif (strcasecmp($urlparts['scheme'], 'https') == 0) {
129
+ $transport_type = 'HTTP';
130
+ } else {
131
+ /* Handle other transport types */
132
+ $transport_type = strtoupper($urlparts['scheme']);
133
+ }
134
+ $transport_class = "SOAP_Transport_$transport_type";
135
+ if (!class_exists($transport_class)) {
136
+ if (!(@include_once('SOAP/Transport/' . basename($transport_type) . '.php'))) {
137
+ return SOAP_Base_Object::_raiseSoapFault("No Transport for {$urlparts['scheme']}");
138
+ }
139
+ }
140
+ if (!class_exists($transport_class)) {
141
+ return SOAP_Base_Object::_raiseSoapFault("No Transport class $transport_class");
142
+ }
143
+
144
+ return new $transport_class($url, $encoding);
145
+ }
146
+
147
+ }
lib/PEAR/SOAP/Transport/HTTP.php ADDED
@@ -0,0 +1,624 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for a HTTP transport layer.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Shane Caraveo <Shane@Caraveo.com>
17
+ * @author Jan Schneider <jan@horde.org>
18
+ * @copyright 2003-2006 The PHP Group
19
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
20
+ * @link http://pear.php.net/package/SOAP
21
+ */
22
+
23
+ /**
24
+ * HTTP Transport class
25
+ *
26
+ * @package SOAP
27
+ * @category Web Services
28
+ */
29
+
30
+ /**
31
+ * Needed Classes
32
+ */
33
+ require_once 'SOAP/Transport.php';
34
+
35
+ /**
36
+ * HTTP Transport for SOAP
37
+ *
38
+ * @access public
39
+ * @package SOAP
40
+ * @author Shane Caraveo <shane@php.net>
41
+ * @author Jan Schneider <jan@horde.org>
42
+ */
43
+ class SOAP_Transport_HTTP extends SOAP_Transport
44
+ {
45
+ /**
46
+ * Basic Auth string.
47
+ *
48
+ * @var array
49
+ */
50
+ var $headers = array();
51
+
52
+ /**
53
+ * Cookies.
54
+ *
55
+ * @var array
56
+ */
57
+ var $cookies;
58
+
59
+ /**
60
+ * Connection timeout in seconds. 0 = none.
61
+ *
62
+ * @var integer
63
+ */
64
+ var $timeout = 4;
65
+
66
+ /**
67
+ * HTTP-Response Content-Type.
68
+ */
69
+ var $result_content_type;
70
+
71
+ var $result_headers = array();
72
+
73
+ var $result_cookies = array();
74
+
75
+ /**
76
+ * SOAP_Transport_HTTP Constructor
77
+ *
78
+ * @access public
79
+ *
80
+ * @param string $url HTTP url to SOAP endpoint.
81
+ * @param string $encoding Encoding to use.
82
+ */
83
+ function SOAP_Transport_HTTP($url, $encoding = SOAP_DEFAULT_ENCODING)
84
+ {
85
+ parent::SOAP_Base('HTTP');
86
+ $this->urlparts = @parse_url($url);
87
+ $this->url = $url;
88
+ $this->encoding = $encoding;
89
+ }
90
+
91
+ /**
92
+ * Sends and receives SOAP data.
93
+ *
94
+ * @access public
95
+ *
96
+ * @param string Outgoing SOAP data.
97
+ * @param array Options.
98
+ *
99
+ * @return string|SOAP_Fault
100
+ */
101
+ function send($msg, $options = array())
102
+ {
103
+ $this->fault = null;
104
+
105
+ if (!$this->_validateUrl()) {
106
+ return $this->fault;
107
+ }
108
+
109
+ if (isset($options['timeout'])) {
110
+ $this->timeout = (int)$options['timeout'];
111
+ }
112
+
113
+ if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
114
+ return $this->_sendHTTP($msg, $options);
115
+ } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
116
+ return $this->_sendHTTPS($msg, $options);
117
+ }
118
+
119
+ return $this->_raiseSoapFault('Invalid url scheme ' . $this->url);
120
+ }
121
+
122
+ /**
123
+ * Sets data for HTTP authentication, creates authorization header.
124
+ *
125
+ * @param string $username Username.
126
+ * @param string $password Response data, minus HTTP headers.
127
+ *
128
+ * @access public
129
+ */
130
+ function setCredentials($username, $password)
131
+ {
132
+ $this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
133
+ }
134
+
135
+ /**
136
+ * Adds a cookie.
137
+ *
138
+ * @access public
139
+ * @param string $name Cookie name.
140
+ * @param mixed $value Cookie value.
141
+ */
142
+ function addCookie($name, $value)
143
+ {
144
+ $this->cookies[$name] = $value;
145
+ }
146
+
147
+ /**
148
+ * Generates the correct headers for the cookies.
149
+ *
150
+ * @access private
151
+ *
152
+ * @param array $options Cookie options. If 'nocookies' is set and true
153
+ * the cookies from the last response are added
154
+ * automatically. 'cookies' is name-value-hash with
155
+ * a list of cookies to add.
156
+ *
157
+ * @return string The cookie header value.
158
+ */
159
+ function _generateCookieHeader($options)
160
+ {
161
+ $this->cookies = array();
162
+
163
+ if (empty($options['nocookies']) &&
164
+ isset($this->result_cookies)) {
165
+ // Add the cookies we got from the last request.
166
+ foreach ($this->result_cookies as $cookie) {
167
+ if ($cookie['domain'] == $this->urlparts['host']) {
168
+ $this->cookies[$cookie['name']] = $cookie['value'];
169
+ }
170
+ }
171
+ }
172
+
173
+ // Add cookies the user wants to set.
174
+ if (isset($options['cookies'])) {
175
+ foreach ($options['cookies'] as $cookie) {
176
+ if ($cookie['domain'] == $this->urlparts['host']) {
177
+ $this->cookies[$cookie['name']] = $cookie['value'];
178
+ }
179
+ }
180
+ }
181
+
182
+ $cookies = '';
183
+ foreach ($this->cookies as $name => $value) {
184
+ if (!empty($cookies)) {
185
+ $cookies .= '; ';
186
+ }
187
+ $cookies .= urlencode($name) . '=' . urlencode($value);
188
+ }
189
+
190
+ return $cookies;
191
+ }
192
+
193
+ /**
194
+ * Validate url data passed to constructor.
195
+ *
196
+ * @access private
197
+ * @return boolean
198
+ */
199
+ function _validateUrl()
200
+ {
201
+ if (!is_array($this->urlparts) ) {
202
+ $this->_raiseSoapFault('Unable to parse URL ' . $this->url);
203
+ return false;
204
+ }
205
+ if (!isset($this->urlparts['host'])) {
206
+ $this->_raiseSoapFault('No host in URL ' . $this->url);
207
+ return false;
208
+ }
209
+ if (!isset($this->urlparts['port'])) {
210
+ if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
211
+ $this->urlparts['port'] = 80;
212
+ } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
213
+ $this->urlparts['port'] = 443;
214
+ }
215
+
216
+ }
217
+ if (isset($this->urlparts['user'])) {
218
+ $this->setCredentials(urldecode($this->urlparts['user']),
219
+ urldecode($this->urlparts['pass']));
220
+ }
221
+ if (!isset($this->urlparts['path']) || !$this->urlparts['path']) {
222
+ $this->urlparts['path'] = '/';
223
+ }
224
+
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * Finds out what the encoding is.
230
+ * Sets the object property accordingly.
231
+ *
232
+ * @access private
233
+ * @param array $headers Headers.
234
+ */
235
+ function _parseEncoding($headers)
236
+ {
237
+ $h = stristr($headers, 'Content-Type');
238
+ preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER);
239
+ $n = count($ct);
240
+ $ct = $ct[$n - 1];
241
+
242
+ // Strip the string of \r.
243
+ $this->result_content_type = str_replace("\r", '', $ct[1]);
244
+
245
+ if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i',
246
+ $this->result_content_type,
247
+ $m)) {
248
+ $this->result_content_type = $m[1];
249
+ if (count($m) > 2) {
250
+ $enc = strtoupper(str_replace('"', '', $m[2]));
251
+ if (in_array($enc, $this->_encodings)) {
252
+ $this->result_encoding = $enc;
253
+ }
254
+ }
255
+ }
256
+
257
+ // Deal with broken servers that don't set content type on faults.
258
+ if (!$this->result_content_type) {
259
+ $this->result_content_type = 'text/xml';
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Parses the headers.
265
+ *
266
+ * @param array $headers The headers.
267
+ */
268
+ function _parseHeaders($headers)
269
+ {
270
+ /* Largely borrowed from HTTP_Request. */
271
+ $this->result_headers = array();
272
+ $headers = split("\r?\n", $headers);
273
+ foreach ($headers as $value) {
274
+ if (strpos($value,':') === false) {
275
+ $this->result_headers[0] = $value;
276
+ continue;
277
+ }
278
+ list($name, $value) = split(':', $value);
279
+ $headername = strtolower($name);
280
+ $headervalue = trim($value);
281
+ $this->result_headers[$headername] = $headervalue;
282
+
283
+ if ($headername == 'set-cookie') {
284
+ // Parse a SetCookie header to fill _cookies array.
285
+ $cookie = array('expires' => null,
286
+ 'domain' => $this->urlparts['host'],
287
+ 'path' => null,
288
+ 'secure' => false);
289
+
290
+ if (!strpos($headervalue, ';')) {
291
+ // Only a name=value pair.
292
+ list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue));
293
+ $cookie['name'] = urldecode($cookie['name']);
294
+ $cookie['value'] = urldecode($cookie['value']);
295
+
296
+ } else {
297
+ // Some optional parameters are supplied.
298
+ $elements = explode(';', $headervalue);
299
+ list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0]));
300
+ $cookie['name'] = urldecode($cookie['name']);
301
+ $cookie['value'] = urldecode($cookie['value']);
302
+
303
+ for ($i = 1; $i < count($elements);$i++) {
304
+ list($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
305
+ if ('secure' == $elName) {
306
+ $cookie['secure'] = true;
307
+ } elseif ('expires' == $elName) {
308
+ $cookie['expires'] = str_replace('"', '', $elValue);
309
+ } elseif ('path' == $elName OR 'domain' == $elName) {
310
+ $cookie[$elName] = urldecode($elValue);
311
+ } else {
312
+ $cookie[$elName] = $elValue;
313
+ }
314
+ }
315
+ }
316
+ $this->result_cookies[] = $cookie;
317
+ }
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Removes HTTP headers from response.
323
+ *
324
+ * @return boolean
325
+ * @access private
326
+ */
327
+ function _parseResponse()
328
+ {
329
+ if (!preg_match("/^(.*?)\r?\n\r?\n(.*)/s",
330
+ $this->incoming_payload,
331
+ $match)) {
332
+ $this->_raiseSoapFault('Invalid HTTP Response');
333
+ return false;
334
+ }
335
+
336
+ $this->response = $match[2];
337
+ // Find the response error, some servers response with 500 for
338
+ // SOAP faults.
339
+ $this->_parseHeaders($match[1]);
340
+
341
+ list(, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s');
342
+ unset($this->result_headers[0]);
343
+
344
+ switch($code) {
345
+ case 100: // Continue
346
+ $this->incoming_payload = $match[2];
347
+ return $this->_parseResponse();
348
+ case 200:
349
+ case 202:
350
+ $this->incoming_payload = trim($match[2]);
351
+ if (!strlen($this->incoming_payload)) {
352
+ /* Valid one-way message response. */
353
+ return true;
354
+ }
355
+ break;
356
+ case 400:
357
+ $this->_raiseSoapFault("HTTP Response $code Bad Request");
358
+ return false;
359
+ case 401:
360
+ $this->_raiseSoapFault("HTTP Response $code Authentication Failed");
361
+ return false;
362
+ case 403:
363
+ $this->_raiseSoapFault("HTTP Response $code Forbidden");
364
+ return false;
365
+ case 404:
366
+ $this->_raiseSoapFault("HTTP Response $code Not Found");
367
+ return false;
368
+ case 407:
369
+ $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required");
370
+ return false;
371
+ case 408:
372
+ $this->_raiseSoapFault("HTTP Response $code Request Timeout");
373
+ return false;
374
+ case 410:
375
+ $this->_raiseSoapFault("HTTP Response $code Gone");
376
+ return false;
377
+ default:
378
+ if ($code >= 400 && $code < 500) {
379
+ $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg");
380
+ return false;
381
+ }
382
+ break;
383
+ }
384
+
385
+ $this->_parseEncoding($match[1]);
386
+
387
+ if ($this->result_content_type == 'application/dime') {
388
+ // XXX quick hack insertion of DIME
389
+ if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) {
390
+ // _decodeDIMEMessage already raised $this->fault
391
+ return false;
392
+ }
393
+ $this->result_content_type = $this->headers['content-type'];
394
+ } elseif (stristr($this->result_content_type, 'multipart/related')) {
395
+ $this->response = $this->incoming_payload;
396
+ if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) {
397
+ // _decodeMimeMessage already raised $this->fault
398
+ return false;
399
+ }
400
+ } elseif ($this->result_content_type != 'text/xml') {
401
+ $this->_raiseSoapFault($this->response);
402
+ return false;
403
+ }
404
+
405
+ // if no content, return false
406
+ return strlen($this->response) > 0;
407
+ }
408
+
409
+ /**
410
+ * Creates an HTTP request, including headers, for the outgoing request.
411
+ *
412
+ * @access private
413
+ *
414
+ * @param string $msg Outgoing SOAP package.
415
+ * @param array $options Options.
416
+ *
417
+ * @return string Outgoing payload.
418
+ */
419
+ function _getRequest($msg, $options)
420
+ {
421
+ $this->headers = array();
422
+
423
+ $action = isset($options['soapaction']) ? $options['soapaction'] : '';
424
+ $fullpath = $this->urlparts['path'];
425
+ if (isset($this->urlparts['query'])) {
426
+ $fullpath .= '?' . $this->urlparts['query'];
427
+ }
428
+ if (isset($this->urlparts['fragment'])) {
429
+ $fullpath .= '#' . $this->urlparts['fragment'];
430
+ }
431
+
432
+ if (isset($options['proxy_host'])) {
433
+ $fullpath = 'http://' . $this->urlparts['host'] . ':' .
434
+ $this->urlparts['port'] . $fullpath;
435
+ }
436
+
437
+ if (isset($options['proxy_user'])) {
438
+ $this->headers['Proxy-Authorization'] = 'Basic ' .
439
+ base64_encode($options['proxy_user'] . ':' .
440
+ $options['proxy_pass']);
441
+ }
442
+
443
+ if (isset($options['user'])) {
444
+ $this->setCredentials($options['user'], $options['pass']);
445
+ }
446
+
447
+ $this->headers['User-Agent'] = $this->_userAgent;
448
+ $this->headers['Host'] = $this->urlparts['host'];
449
+ $this->headers['Content-Type'] = "text/xml; charset=$this->encoding";
450
+ $this->headers['Content-Length'] = strlen($msg);
451
+ $this->headers['SOAPAction'] = '"' . $action . '"';
452
+ $this->headers['Connection'] = 'close';
453
+
454
+ if (isset($options['headers'])) {
455
+ $this->headers = array_merge($this->headers, $options['headers']);
456
+ }
457
+
458
+ $cookies = $this->_generateCookieHeader($options);
459
+ if ($cookies) {
460
+ $this->headers['Cookie'] = $cookies;
461
+ }
462
+
463
+ $headers = '';
464
+ foreach ($this->headers as $k => $v) {
465
+ $headers .= "$k: $v\r\n";
466
+ }
467
+ $this->outgoing_payload = "POST $fullpath HTTP/1.0\r\n" . $headers .
468
+ "\r\n" . $msg;
469
+
470
+ return $this->outgoing_payload;
471
+ }
472
+
473
+ /**
474
+ * Sends the outgoing HTTP request and reads and parses the response.
475
+ *
476
+ * @access private
477
+ *
478
+ * @param string $msg Outgoing SOAP package.
479
+ * @param array $options Options.
480
+ *
481
+ * @return string Response data without HTTP headers.
482
+ */
483
+ function _sendHTTP($msg, $options)
484
+ {
485
+ $this->incoming_payload = '';
486
+ $this->_getRequest($msg, $options);
487
+ $host = $this->urlparts['host'];
488
+ $port = $this->urlparts['port'];
489
+ if (isset($options['proxy_host'])) {
490
+ $host = $options['proxy_host'];
491
+ $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
492
+ }
493
+ // Send.
494
+ if ($this->timeout > 0) {
495
+ $fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
496
+ } else {
497
+ $fp = @fsockopen($host, $port, $this->errno, $this->errmsg);
498
+ }
499
+ if (!$fp) {
500
+ return $this->_raiseSoapFault("Connect Error to $host:$port");
501
+ }
502
+ if ($this->timeout > 0) {
503
+ // some builds of PHP do not support this, silence the warning
504
+ @socket_set_timeout($fp, $this->timeout);
505
+ }
506
+ if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
507
+ return $this->_raiseSoapFault("Error POSTing Data to $host");
508
+ }
509
+
510
+ // get reponse
511
+ // XXX time consumer
512
+ do {
513
+ $data = fread($fp, 4096);
514
+ $_tmp_status = socket_get_status($fp);
515
+ if ($_tmp_status['timed_out']) {
516
+ return $this->_raiseSoapFault("Timed out read from $host");
517
+ } else {
518
+ $this->incoming_payload .= $data;
519
+ }
520
+ } while (!$_tmp_status['eof']);
521
+
522
+ fclose($fp);
523
+
524
+ if (!$this->_parseResponse()) {
525
+ return $this->fault;
526
+ }
527
+ return $this->response;
528
+ }
529
+
530
+ /**
531
+ * Sends the outgoing HTTPS request and reads and parses the response.
532
+ *
533
+ * @access private
534
+ *
535
+ * @param string $msg Outgoing SOAP package.
536
+ * @param array $options Options.
537
+ *
538
+ * @return string Response data without HTTP headers.
539
+ */
540
+ function _sendHTTPS($msg, $options)
541
+ {
542
+ /* Check if the required curl extension is installed. */
543
+ if (!extension_loaded('curl')) {
544
+ return $this->_raiseSoapFault('CURL Extension is required for HTTPS');
545
+ }
546
+
547
+ $ch = curl_init();
548
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
549
+ if (isset($options['proxy_host'])) {
550
+ $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
551
+ curl_setopt($ch, CURLOPT_PROXY,
552
+ $options['proxy_host'] . ':' . $port);
553
+ }
554
+ if (isset($options['proxy_user'])) {
555
+ curl_setopt($ch, CURLOPT_PROXYUSERPWD,
556
+ $options['proxy_user'] . ':' . $options['proxy_pass']);
557
+ }
558
+
559
+ if (isset($options['user'])) {
560
+ curl_setopt($ch, CURLOPT_USERPWD,
561
+ $options['user'] . ':' . $options['pass']);
562
+ }
563
+
564
+ $headers = array();
565
+ $action = isset($options['soapaction']) ? $options['soapaction'] : '';
566
+ $headers['Content-Type'] = "text/xml; charset=$this->encoding";
567
+ $headers['SOAPAction'] = '"' . $action . '"';
568
+ if (isset($options['headers'])) {
569
+ $headers = array_merge($headers, $options['headers']);
570
+ }
571
+ foreach ($headers as $header => $value) {
572
+ $headers[$header] = $header . ': ' . $value;
573
+ }
574
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
575
+ curl_setopt($ch, CURLOPT_USERAGENT, $this->_userAgent);
576
+
577
+ if ($this->timeout) {
578
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
579
+ }
580
+
581
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $msg);
582
+ curl_setopt($ch, CURLOPT_URL, $this->url);
583
+ curl_setopt($ch, CURLOPT_POST, 1);
584
+ curl_setopt($ch, CURLOPT_FAILONERROR, 0);
585
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
586
+ curl_setopt($ch, CURLOPT_HEADER, 1);
587
+ if (defined('CURLOPT_HTTP_VERSION')) {
588
+ curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
589
+ }
590
+ if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
591
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
592
+ }
593
+ $cookies = $this->_generateCookieHeader($options);
594
+ if ($cookies) {
595
+ curl_setopt($ch, CURLOPT_COOKIE, $cookies);
596
+ }
597
+
598
+ if (isset($options['curl'])) {
599
+ foreach ($options['curl'] as $key => $val) {
600
+ curl_setopt($ch, $key, $val);
601
+ }
602
+ }
603
+
604
+ // Save the outgoing XML. This doesn't quite match _sendHTTP as CURL
605
+ // generates the headers, but having the XML is usually the most
606
+ // important part for tracing/debugging.
607
+ $this->outgoing_payload = $msg;
608
+
609
+ $this->incoming_payload = curl_exec($ch);
610
+ if (!$this->incoming_payload) {
611
+ $m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch);
612
+ curl_close($ch);
613
+ return $this->_raiseSoapFault($m);
614
+ }
615
+ curl_close($ch);
616
+
617
+ if (!$this->_parseResponse()) {
618
+ return $this->fault;
619
+ }
620
+
621
+ return $this->response;
622
+ }
623
+
624
+ }
lib/PEAR/SOAP/Value.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for converting values between SOAP and PHP.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
17
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
18
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
19
+ * @author Jan Schneider <jan@horde.org> Maintenance
20
+ * @copyright 2003-2007 The PHP Group
21
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
22
+ * @link http://pear.php.net/package/SOAP
23
+ */
24
+
25
+ require_once 'SOAP/Base.php';
26
+
27
+ /**
28
+ * SOAP::Value
29
+ *
30
+ * This class converts values between PHP and SOAP.
31
+ *
32
+ * Originally based on SOAPx4 by Dietrich Ayala
33
+ * http://dietrich.ganx4.com/soapx4
34
+ *
35
+ * @access public
36
+ * @package SOAP
37
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
38
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
39
+ */
40
+ class SOAP_Value
41
+ {
42
+ /**
43
+ * The actual value.
44
+ *
45
+ * @var mixed
46
+ */
47
+ var $value = null;
48
+
49
+ /**
50
+ * QName instance representing the value name.
51
+ *
52
+ * @var QName
53
+ */
54
+ var $nqn;
55
+
56
+ /**
57
+ * The value name, without namespace information.
58
+ *
59
+ * @var string
60
+ */
61
+ var $name = '';
62
+
63
+ /**
64
+ * The namespace of the value name.
65
+ *
66
+ * @var string
67
+ */
68
+ var $namespace = '';
69
+
70
+ /**
71
+ * QName instance representing the value type.
72
+ *
73
+ * @var QName
74
+ */
75
+ var $tqn;
76
+
77
+ /**
78
+ * The value type, without namespace information.
79
+ *
80
+ * @var string
81
+ */
82
+ var $type = '';
83
+
84
+ /**
85
+ * The namespace of the value type.
86
+ *
87
+ * @var string
88
+ */
89
+ var $type_namespace = '';
90
+
91
+ /**
92
+ * The type of the array elements, if this value is an array.
93
+ *
94
+ * @var string
95
+ */
96
+ var $arrayType = '';
97
+
98
+ /**
99
+ * A hash of additional attributes.
100
+ *
101
+ * @see SOAP_Value()
102
+ * @var array
103
+ */
104
+ var $attributes = array();
105
+
106
+ /**
107
+ * List of encoding and serialization options.
108
+ *
109
+ * @see SOAP_Value()
110
+ * @var array
111
+ */
112
+ var $options = array();
113
+
114
+ /**
115
+ * Constructor.
116
+ *
117
+ * @param string $name Name of the SOAP value {namespace}name.
118
+ * @param mixed $type SOAP value {namespace}type. Determined
119
+ * automatically if not set.
120
+ * @param mixed $value Value to set.
121
+ * @param array $attributes A has of additional XML attributes to be
122
+ * added to the serialized value.
123
+ * @param array $options A list of encoding and serialization options:
124
+ * - 'attachment': array with information about
125
+ * the attachment
126
+ * - 'soap_encoding': defines encoding for SOAP
127
+ * message part of a MIME encoded SOAP request
128
+ * (default: base64)
129
+ * - 'keep_arrays_flat': use the tag name
130
+ * multiple times for each element when
131
+ * passing in an array in literal mode
132
+ * - 'no_type_prefix': supress adding of the
133
+ * namespace prefix
134
+ */
135
+ function SOAP_Value($name = '', $type = false, $value = null,
136
+ $attributes = array(), $options = array())
137
+ {
138
+ $this->nqn = new QName($name);
139
+ $this->name = $this->nqn->name;
140
+ $this->namespace = $this->nqn->namespace;
141
+ if ($type) {
142
+ $this->tqn = new QName($type);
143
+ $this->type = $this->tqn->name;
144
+ $this->type_namespace = $this->tqn->namespace;
145
+ }
146
+ $this->value = $value;
147
+ $this->attributes = $attributes;
148
+ $this->options = $options;
149
+ }
150
+
151
+ /**
152
+ * Serializes this value.
153
+ *
154
+ * @param SOAP_Base $serializer A SOAP_Base instance or subclass to
155
+ * serialize with.
156
+ *
157
+ * @return string XML representation of $this.
158
+ */
159
+ function serialize(&$serializer)
160
+ {
161
+ return $serializer->_serializeValue($this->value,
162
+ $this->nqn,
163
+ $this->tqn,
164
+ $this->options,
165
+ $this->attributes,
166
+ $this->arrayType);
167
+ }
168
+
169
+ }
170
+
171
+ /**
172
+ * This class converts values between PHP and SOAP. It is a simple wrapper
173
+ * around SOAP_Value, adding support for SOAP actor and mustunderstand
174
+ * parameters.
175
+ *
176
+ * Originally based on SOAPx4 by Dietrich Ayala
177
+ * http://dietrich.ganx4.com/soapx4
178
+ *
179
+ * @access public
180
+ * @package SOAP
181
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
182
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
183
+ */
184
+ class SOAP_Header extends SOAP_Value
185
+ {
186
+ /**
187
+ * Constructor
188
+ *
189
+ * @param string $name Name of the SOAP value {namespace}name.
190
+ * @param mixed $type SOAP value {namespace}type. Determined
191
+ * automatically if not set.
192
+ * @param mixed $value Value to set
193
+ * @param integer $mustunderstand Zero or one.
194
+ * @param mixed $attributes Attributes.
195
+ */
196
+ function SOAP_Header($name = '', $type, $value, $mustunderstand = 0,
197
+ $attributes = array())
198
+ {
199
+ if (!is_array($attributes)) {
200
+ $actor = $attributes;
201
+ $attributes = array();
202
+ }
203
+
204
+ parent::SOAP_Value($name, $type, $value, $attributes);
205
+
206
+ if (isset($actor)) {
207
+ $this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = $actor;
208
+ } elseif (!isset($this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'])) {
209
+ $this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = 'http://schemas.xmlsoap.org/soap/actor/next';
210
+ }
211
+ $this->attributes[SOAP_BASE::SOAPENVPrefix().':mustUnderstand'] = (int)$mustunderstand;
212
+ }
213
+
214
+ }
215
+
216
+ /**
217
+ * This class handles MIME attachements per W3C's Note on Soap Attachements at
218
+ * http://www.w3.org/TR/SOAP-attachments
219
+ *
220
+ * @access public
221
+ * @package SOAP
222
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
223
+ */
224
+ class SOAP_Attachment extends SOAP_Value
225
+ {
226
+ /**
227
+ * Constructor.
228
+ *
229
+ * @param string $name Name of the SOAP value <value_name>
230
+ * @param string $type The attachment's MIME type.
231
+ * @param string $filename The attachment's file name. Ignored if $file
232
+ * is provide.
233
+ * @param string $file The attachment data.
234
+ * @param array $attributes Attributes.
235
+ */
236
+ function SOAP_Attachment($name = '', $type = 'application/octet-stream',
237
+ $filename, $file = null, $attributes = null)
238
+ {
239
+ parent::SOAP_Value($name, null, null);
240
+
241
+ $filedata = $file === null ? $this->_file2str($filename) : $file;
242
+ $filename = basename($filename);
243
+ if (PEAR::isError($filedata)) {
244
+ $this->options['attachment'] = $filedata;
245
+ return;
246
+ }
247
+
248
+ $cid = md5(uniqid(time()));
249
+
250
+ $this->attributes = $attributes;
251
+ $this->attributes['href'] = 'cid:' . $cid;
252
+
253
+ $this->options['attachment'] = array('body' => $filedata,
254
+ 'disposition' => $filename,
255
+ 'content_type' => $type,
256
+ 'encoding' => 'base64',
257
+ 'cid' => $cid);
258
+ }
259
+
260
+ /**
261
+ * Returns the contents of the given file name as string.
262
+ *
263
+ * @access private
264
+ *
265
+ * @param string $file_name The file location.
266
+ *
267
+ * @return string The file data or a PEAR_Error.
268
+ */
269
+ function _file2str($file_name)
270
+ {
271
+ if (!is_readable($file_name)) {
272
+ return PEAR::raiseError('File is not readable: ' . $file_name);
273
+ }
274
+
275
+ if (function_exists('file_get_contents')) {
276
+ return file_get_contents($file_name);
277
+ }
278
+
279
+ if (!$fd = fopen($file_name, 'rb')) {
280
+ return PEAR::raiseError('Could not open ' . $file_name);
281
+ }
282
+ $cont = fread($fd, filesize($file_name));
283
+ fclose($fd);
284
+
285
+ return $cont;
286
+ }
287
+
288
+ }
lib/PEAR/SOAP/WSDL.php ADDED
@@ -0,0 +1,2294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the code for dealing with WSDL access and services.
4
+ *
5
+ * PHP versions 4 and 5
6
+ *
7
+ * LICENSE: This source file is subject to version 2.02 of the PHP license,
8
+ * that is bundled with this package in the file LICENSE, and is available at
9
+ * through the world-wide-web at http://www.php.net/license/2_02.txt. If you
10
+ * did not receive a copy of the PHP license and are unable to obtain it
11
+ * through the world-wide-web, please send a note to license@php.net so we can
12
+ * mail you a copy immediately.
13
+ *
14
+ * @category Web Services
15
+ * @package SOAP
16
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
17
+ * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more
18
+ * @author Chuck Hagenbuch <chuck@horde.org> Maintenance
19
+ * @author Jan Schneider <jan@horde.org> Maintenance
20
+ * @copyright 2003-2005 The PHP Group
21
+ * @license http://www.php.net/license/2_02.txt PHP License 2.02
22
+ * @link http://pear.php.net/package/SOAP
23
+ */
24
+
25
+ require_once 'SOAP/Base.php';
26
+ require_once 'SOAP/Fault.php';
27
+ require_once 'HTTP/Request.php';
28
+
29
+ define('WSDL_CACHE_MAX_AGE', 43200);
30
+
31
+ /**
32
+ * This class parses WSDL files, and can be used by SOAP::Client to properly
33
+ * register soap values for services.
34
+ *
35
+ * Originally based on SOAPx4 by Dietrich Ayala
36
+ * http://dietrich.ganx4.com/soapx4
37
+ *
38
+ * @todo
39
+ * - refactor namespace handling ($namespace/$ns)
40
+ * - implement IDL type syntax declaration so we can generate WSDL
41
+ *
42
+ * @access public
43
+ * @package SOAP
44
+ * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
45
+ * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
46
+ */
47
+ class SOAP_WSDL extends SOAP_Base
48
+ {
49
+ var $tns = null;
50
+ var $definition = array();
51
+ var $namespaces = array();
52
+ var $ns = array();
53
+ var $xsd = SOAP_XML_SCHEMA_VERSION;
54
+ var $complexTypes = array();
55
+ var $elements = array();
56
+ var $messages = array();
57
+ var $portTypes = array();
58
+ var $bindings = array();
59
+ var $imports = array();
60
+ var $services = array();
61
+ var $service = '';
62
+
63
+ /**
64
+ * URL to WSDL file.
65
+ *
66
+ * @var string
67
+ */
68
+ var $uri;
69
+
70
+ /**
71
+ * Parse documentation in the WSDL?
72
+ *
73
+ * @var boolean
74
+ */
75
+ var $docs;
76
+
77
+ /**
78
+ * Proxy parameters.
79
+ *
80
+ * @var array
81
+ */
82
+ var $proxy;
83
+
84
+ /**
85
+ * Enable tracing in the generated proxy class?
86
+ *
87
+ * @var boolean
88
+ */
89
+ var $trace = false;
90
+
91
+ /**
92
+ * Use WSDL cache?
93
+ *
94
+ * @var boolean
95
+ */
96
+ var $cacheUse;
97
+
98
+ /**
99
+ * WSDL cache directory.
100
+ *
101
+ * @var string
102
+ */
103
+ var $cacheDir;
104
+
105
+ /**
106
+ * Cache maximum lifetime (in seconds).
107
+ *
108
+ * @var integer
109
+ */
110
+ var $cacheMaxAge;
111
+
112
+ /**
113
+ * Class to use for WSDL parsing. Can be overridden for special cases,
114
+ * subclasses, etc.
115
+ *
116
+ * @var string
117
+ */
118
+ var $wsdlParserClass = 'SOAP_WSDL_Parser';
119
+
120
+ /**
121
+ * Reserved PHP keywords.
122
+ *
123
+ * @link http://www.php.net/manual/en/reserved.php
124
+ *
125
+ * @var array
126
+ */
127
+ var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case',
128
+ 'catch', 'cfunction', 'class', 'clone', 'const',
129
+ 'continue', 'declare', 'default', 'die', 'do',
130
+ 'echo', 'else', 'elseif', 'empty', 'enddeclare',
131
+ 'endfor', 'endforeach', 'endif', 'endswitch',
132
+ 'endwhile', 'eval', 'exception', 'exit', 'extends',
133
+ 'final', 'for', 'foreach', 'function', 'global',
134
+ 'if', 'implements', 'include', 'include_once',
135
+ 'interface', 'isset', 'list', 'new', 'old_function',
136
+ 'or', 'php_user_filter', 'print', 'private',
137
+ 'protected', 'public', 'require', 'require_once',
138
+ 'return', 'static', 'switch', 'this', 'throw',
139
+ 'try', 'unset', 'use', 'var', 'while', 'xor');
140
+
141
+ /**
142
+ * Regular expressions for invalid PHP labels.
143
+ *
144
+ * @link http://www.php.net/manual/en/language.variables.php.
145
+ *
146
+ * @var string
147
+ */
148
+ var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/');
149
+
150
+ /**
151
+ * SOAP_WSDL constructor.
152
+ *
153
+ * @param string $wsdl_uri URL to WSDL file.
154
+ * @param array $proxy Options for HTTP_Request class
155
+ * @see HTTP_Request.
156
+ * @param boolean|string $cacheUse Use WSDL caching? The cache directory
157
+ * if a string.
158
+ * @param integer $cacheMaxAge Cache maximum lifetime (in seconds).
159
+ * @param boolean $docs Parse documentation in the WSDL?
160
+ *
161
+ * @access public
162
+ */
163
+ function SOAP_WSDL($wsdl_uri = false,
164
+ $proxy = array(),
165
+ $cacheUse = false,
166
+ $cacheMaxAge = WSDL_CACHE_MAX_AGE,
167
+ $docs = false)
168
+ {
169
+ parent::SOAP_Base('WSDL');
170
+ $this->uri = $wsdl_uri;
171
+ $this->proxy = $proxy;
172
+ $this->cacheUse = !empty($cacheUse);
173
+ $this->cacheMaxAge = $cacheMaxAge;
174
+ $this->docs = $docs;
175
+ if (is_string($cacheUse)) {
176
+ $this->cacheDir = $cacheUse;
177
+ }
178
+
179
+ if ($wsdl_uri) {
180
+ if (!PEAR::isError($this->parseURL($wsdl_uri))) {
181
+ reset($this->services);
182
+ $this->service = key($this->services);
183
+ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * @deprecated Use setService().
189
+ */
190
+ function set_service($service)
191
+ {
192
+ $this->setService($service);
193
+ }
194
+
195
+ /**
196
+ * Sets the service currently to be used.
197
+ *
198
+ * @param string $service An (existing) service name.
199
+ */
200
+ function setService($service)
201
+ {
202
+ if (array_key_exists($service, $this->services)) {
203
+ $this->service = $service;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Fills the WSDL array tree with data from a WSDL file.
209
+ *
210
+ * @param string $wsdl_uri URL to WSDL file.
211
+ */
212
+ function parseURL($wsdl_uri)
213
+ {
214
+ $parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs);
215
+
216
+ if ($parser->fault) {
217
+ $this->_raiseSoapFault($parser->fault);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Fills the WSDL array tree with data from one or more PHP class objects.
223
+ *
224
+ * @param mixed $wsdl_obj An object or array of objects to add to
225
+ * the internal WSDL tree.
226
+ * @param string $targetNamespace The target namespace of schema types
227
+ * etc.
228
+ * @param string $service_name Name of the WSDL service.
229
+ * @param string $service_desc Optional description of the WSDL
230
+ * service.
231
+ */
232
+ function parseObject($wsdl_obj, $targetNamespace, $service_name,
233
+ $service_desc = '')
234
+ {
235
+ $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this,
236
+ $targetNamespace, $service_name,
237
+ $service_desc);
238
+
239
+ if ($parser->fault) {
240
+ $this->_raiseSoapFault($parser->fault);
241
+ }
242
+ }
243
+
244
+ function getEndpoint($portName)
245
+ {
246
+ if ($this->_isfault()) {
247
+ return $this->_getfault();
248
+ }
249
+
250
+ return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
251
+ ? $this->services[$this->service]['ports'][$portName]['address']['location']
252
+ : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri);
253
+ }
254
+
255
+ function _getPortName($operation, $service)
256
+ {
257
+ if (isset($this->services[$service]['ports'])) {
258
+ $ports = $this->services[$service]['ports'];
259
+ foreach ($ports as $port => $portAttrs) {
260
+ $type = $ports[$port]['type'];
261
+ if ($type == 'soap' &&
262
+ isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
263
+ return $port;
264
+ }
265
+ }
266
+ }
267
+ return null;
268
+ }
269
+
270
+ /**
271
+ * Finds the name of the first port that contains an operation of name
272
+ * $operation. Always returns a SOAP portName.
273
+ */
274
+ function getPortName($operation, $service = null)
275
+ {
276
+ if ($this->_isfault()) {
277
+ return $this->_getfault();
278
+ }
279
+
280
+ if (!$service) {
281
+ $service = $this->service;
282
+ }
283
+ if (isset($this->services[$service]['ports'])) {
284
+ if ($portName = $this->_getPortName($operation, $service)) {
285
+ return $portName;
286
+ }
287
+ }
288
+ // Try any service in the WSDL.
289
+ foreach ($this->services as $serviceName => $service) {
290
+ if (isset($this->services[$serviceName]['ports'])) {
291
+ if ($portName = $this->_getPortName($operation, $serviceName)) {
292
+ $this->service = $serviceName;
293
+ return $portName;
294
+ }
295
+ }
296
+ }
297
+ return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri);
298
+ }
299
+
300
+ function getOperationData($portName, $operation)
301
+ {
302
+ if ($this->_isfault()) {
303
+ return $this->_getfault();
304
+ }
305
+
306
+ if (!isset($this->services[$this->service]['ports'][$portName]['binding']) ||
307
+ !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) {
308
+ return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri);
309
+ }
310
+
311
+ // Get operation data from binding.
312
+ if (is_array($this->bindings[$binding]['operations'][$operation])) {
313
+ $opData = $this->bindings[$binding]['operations'][$operation];
314
+ }
315
+ // Get operation data from porttype.
316
+ $portType = $this->bindings[$binding]['type'];
317
+ if (!$portType) {
318
+ return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri);
319
+ }
320
+ if (is_array($type = $this->portTypes[$portType][$operation])) {
321
+ if (isset($type['parameterOrder'])) {
322
+ $opData['parameterOrder'] = $type['parameterOrder'];
323
+ }
324
+ $opData['input'] = array_merge($opData['input'], $type['input']);
325
+ $opData['output'] = array_merge($opData['output'], $type['output']);
326
+ }
327
+ if (!$opData)
328
+ return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri);
329
+ $opData['parameters'] = false;
330
+ if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
331
+ $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace'];
332
+ // Message data from messages.
333
+ $inputMsg = $opData['input']['message'];
334
+ if (is_array($this->messages[$inputMsg])) {
335
+ foreach ($this->messages[$inputMsg] as $pname => $pattrs) {
336
+ if ($opData['style'] == 'document' &&
337
+ $opData['input']['use'] == 'literal' &&
338
+ $pname == 'parameters') {
339
+ $opData['parameters'] = true;
340
+ $opData['namespace'] = $this->namespaces[$pattrs['namespace']];
341
+ $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
342
+ if (isset($el['elements'])) {
343
+ foreach ($el['elements'] as $elname => $elattrs) {
344
+ $opData['input']['parts'][$elname] = $elattrs;
345
+ }
346
+ }
347
+ } else {
348
+ $opData['input']['parts'][$pname] = $pattrs;
349
+ }
350
+ }
351
+ }
352
+ $outputMsg = $opData['output']['message'];
353
+ if (is_array($this->messages[$outputMsg])) {
354
+ foreach ($this->messages[$outputMsg] as $pname => $pattrs) {
355
+ if ($opData['style'] == 'document' &&
356
+ $opData['output']['use'] == 'literal' &&
357
+ $pname == 'parameters') {
358
+
359
+ $el = $this->elements[$pattrs['namespace']][$pattrs['type']];
360
+ if (isset($el['elements'])) {
361
+ foreach ($el['elements'] as $elname => $elattrs) {
362
+ $opData['output']['parts'][$elname] = $elattrs;
363
+ }
364
+ }
365
+ } else {
366
+ $opData['output']['parts'][$pname] = $pattrs;
367
+ }
368
+ }
369
+ }
370
+ return $opData;
371
+ }
372
+
373
+ function matchMethod(&$operation)
374
+ {
375
+ if ($this->_isfault()) {
376
+ return $this->_getfault();
377
+ }
378
+
379
+ // Overloading lowercases function names :(
380
+ foreach ($this->services[$this->service]['ports'] as $portAttrs) {
381
+ foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) {
382
+ if (strcasecmp($op, $operation) == 0) {
383
+ $operation = $op;
384
+ }
385
+ }
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Given a datatype, what function handles the processing?
391
+ *
392
+ * This is used for doc/literal requests where we receive a datatype, and
393
+ * we need to pass it to a method in out server class.
394
+ *
395
+ * @param string $datatype
396
+ * @param string $namespace
397
+ * @return string
398
+ * @access public
399
+ */
400
+ function getDataHandler($datatype, $namespace)
401
+ {
402
+ // See if we have an element by this name.
403
+ if (isset($this->namespaces[$namespace])) {
404
+ $namespace = $this->namespaces[$namespace];
405
+ }
406
+
407
+ if (!isset($this->ns[$namespace])) {
408
+ return null;
409
+ }
410
+
411
+ $nsp = $this->ns[$namespace];
412
+ //if (!isset($this->elements[$nsp]))
413
+ // $nsp = $this->namespaces[$nsp];
414
+ if (!isset($this->elements[$nsp][$datatype])) {
415
+ return null;
416
+ }
417
+
418
+ $checkmessages = array();
419
+ // Find what messages use this datatype.
420
+ foreach ($this->messages as $messagename => $message) {
421
+ foreach ($message as $part) {
422
+ if ($part['type'] == $datatype) {
423
+ $checkmessages[] = $messagename;
424
+ break;
425
+ }
426
+ }
427
+ }
428
+ // Find the operation that uses this message.
429
+ foreach($this->portTypes as $porttype) {
430
+ foreach ($porttype as $opname => $opinfo) {
431
+ foreach ($checkmessages as $messagename) {
432
+ if ($opinfo['input']['message'] == $messagename) {
433
+ return $opname;
434
+ }
435
+ }
436
+ }
437
+ }
438
+
439
+ return null;
440
+ }
441
+
442
+ function getSoapAction($portName, $operation)
443
+ {
444
+ if ($this->_isfault()) {
445
+ return $this->_getfault();
446
+ }
447
+
448
+ if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
449
+ return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
450
+ }
451
+
452
+ return false;
453
+ }
454
+
455
+ function getNamespace($portName, $operation)
456
+ {
457
+ if ($this->_isfault()) {
458
+ return $this->_getfault();
459
+ }
460
+
461
+ if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
462
+ return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
463
+ }
464
+
465
+ return false;
466
+ }
467
+
468
+ function getNamespaceAttributeName($namespace)
469
+ {
470
+ /* If it doesn't exist at first, flip the array and check again. */
471
+ if (empty($this->ns[$namespace])) {
472
+ $this->ns = array_flip($this->namespaces);
473
+ }
474
+
475
+ /* If it doesn't exist now, add it. */
476
+ if (empty($this->ns[$namespace])) {
477
+ return $this->addNamespace($namespace);
478
+ }
479
+
480
+ return $this->ns[$namespace];
481
+ }
482
+
483
+ function addNamespace($namespace)
484
+ {
485
+ if (!empty($this->ns[$namespace])) {
486
+ return $this->ns[$namespace];
487
+ }
488
+
489
+ $n = count($this->ns);
490
+ $attr = 'ns' . $n;
491
+ $this->namespaces['ns' . $n] = $namespace;
492
+ $this->ns[$namespace] = $attr;
493
+
494
+ return $attr;
495
+ }
496
+
497
+ function _validateString($string)
498
+ {
499
+ return preg_match('/^[\w_:#\/]+$/', $string);
500
+ }
501
+
502
+ function _addArg(&$args, &$argarray, $argname)
503
+ {
504
+ if ($args) {
505
+ $args .= ', ';
506
+ }
507
+ $args .= '$' . $argname;
508
+ if (!$this->_validateString($argname)) {
509
+ return;
510
+ }
511
+ if ($argarray) {
512
+ $argarray .= ', ';
513
+ }
514
+ $argarray .= "'$argname' => $" . $argname;
515
+ }
516
+
517
+ function _elementArg(&$args, &$argarray, &$_argtype, $_argname)
518
+ {
519
+ $comments = '';
520
+ $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
521
+ $tns = isset($this->ns[$el['namespace']])
522
+ ? $this->ns[$el['namespace']]
523
+ : $_argtype['namespace'];
524
+
525
+ if (!empty($el['complex']) ||
526
+ (isset($el['type']) &&
527
+ isset($this->complexTypes[$tns][$el['type']]))) {
528
+ // The element is a complex type.
529
+ $comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
530
+ $attrname = "{$_argtype['type']}_attr";
531
+ if (isset($el['type']) &&
532
+ isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
533
+ $comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
534
+ }
535
+ $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
536
+ $comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
537
+ $this->_addArg($args, $argarray, $_argtype['type']);
538
+ if (isset($el['type']) &&
539
+ isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
540
+ if ($args) {
541
+ $args .= ', ';
542
+ }
543
+ $args .= '$' . $attrname;
544
+ }
545
+ } elseif (isset($el['elements'])) {
546
+ foreach ($el['elements'] as $ename => $element) {
547
+ $comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
548
+ (isset($element['type']) ? $element['type'] : false) .
549
+ "', \$$ename);\n";
550
+ $this->_addArg($args, $argarray, $ename);
551
+ }
552
+ } else {
553
+ $comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
554
+ $this->_addArg($args, $argarray, $_argname);
555
+ }
556
+
557
+ return $comments;
558
+ }
559
+
560
+ function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname)
561
+ {
562
+ $comments = '';
563
+ if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
564
+ $comments = " // $_argname is a ComplexType {$_argtype['type']},\n" .
565
+ " // refer to wsdl for more info\n";
566
+ if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
567
+ $comments .= " // $_argname may require attributes, refer to wsdl for more info\n";
568
+ }
569
+ $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type'];
570
+ $comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
571
+ }
572
+
573
+ $this->_addArg($args, $argarray, $_argname);
574
+
575
+ return $comments;
576
+ }
577
+
578
+ /**
579
+ * Generates stub code from the WSDL that can be saved to a file or eval'd
580
+ * into existence.
581
+ */
582
+ function generateProxyCode($port = '', $classname = '')
583
+ {
584
+ if ($this->_isfault()) {
585
+ return $this->_getfault();
586
+ }
587
+
588
+ $multiport = count($this->services[$this->service]['ports']) > 1;
589
+ if (!$port) {
590
+ reset($this->services[$this->service]['ports']);
591
+ $port = current($this->services[$this->service]['ports']);
592
+ }
593
+ // XXX currently do not support HTTP ports
594
+ if ($port['type'] != 'soap') {
595
+ return null;
596
+ }
597
+
598
+ // XXX currentPort is BAD
599
+ $clienturl = $port['address']['location'];
600
+ if (!$classname) {
601
+ if ($multiport || $port) {
602
+ $classname = 'WebService_' . $this->service . '_' . $port['name'];
603
+ } else {
604
+ $classname = 'WebService_' . $this->service;
605
+ }
606
+ $classname = $this->_sanitize($classname);
607
+ }
608
+
609
+ if (!$this->_validateString($classname)) {
610
+ return null;
611
+ }
612
+
613
+ if (is_array($this->proxy) && count($this->proxy)) {
614
+ $class = "class $classname extends SOAP_Client\n{\n" .
615
+ " function $classname(\$path = '$clienturl')\n {\n" .
616
+ " \$this->SOAP_Client(\$path, 0, 0,\n" .
617
+ ' array(';
618
+
619
+ foreach ($this->proxy as $key => $val) {
620
+ if (is_array($val)) {
621
+ $class .= "'$key' => array(";
622
+ foreach ($val as $key2 => $val2) {
623
+ $class .= "'$key2' => '$val2', ";
624
+ }
625
+ $class .= ')';
626
+ } else {
627
+ $class .= "'$key' => '$val', ";
628
+ }
629
+ }
630
+ $class .= "));\n }\n";
631
+ $class = str_replace(', ))', '))', $class);
632
+ } else {
633
+ $class = "class $classname extends SOAP_Client\n{\n" .
634
+ " function $classname(\$path = '$clienturl')\n {\n" .
635
+ " \$this->SOAP_Client(\$path, 0);\n" .
636
+ " }\n";
637
+ }
638
+
639
+ // Get the binding, from that get the port type.
640
+ $primaryBinding = $port['binding'];
641
+ $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding);
642
+ $portType = $this->bindings[$primaryBinding]['type'];
643
+ $portType = preg_replace("/^(.*:)/", '', $portType);
644
+ $style = $this->bindings[$primaryBinding]['style'];
645
+
646
+ // XXX currentPortType is BAD
647
+ foreach ($this->portTypes[$portType] as $opname => $operation) {
648
+ $binding = $this->bindings[$primaryBinding]['operations'][$opname];
649
+ if (isset($binding['soapAction'])) {
650
+ $soapaction = $binding['soapAction'];
651
+ } else {
652
+ $soapaction = null;
653
+ }
654
+ if (isset($binding['style'])) {
655
+ $opstyle = $binding['style'];
656
+ } else {
657
+ $opstyle = $style;
658
+ }
659
+ $use = $binding['input']['use'];
660
+ if ($use == 'encoded') {
661
+ $namespace = $binding['input']['namespace'];
662
+ } else {
663
+ $bindingType = $this->bindings[$primaryBinding]['type'];
664
+ $ns = $this->portTypes[$bindingType][$opname]['input']['namespace'];
665
+ $namespace = $this->namespaces[$ns];
666
+ }
667
+
668
+ $args = '';
669
+ $argarray = '';
670
+ $comments = '';
671
+ $wrappers = '';
672
+ foreach ($operation['input'] as $argname => $argtype) {
673
+ if ($argname == 'message') {
674
+ foreach ($this->messages[$argtype] as $_argname => $_argtype) {
675
+ $_argname = $this->_sanitize($_argname);
676
+ if ($opstyle == 'document' && $use == 'literal' &&
677
+ $_argtype['name'] == 'parameters') {
678
+ // The type or element refered to is used for
679
+ // parameters.
680
+ $elattrs = null;
681
+ $el = $this->elements[$_argtype['namespace']][$_argtype['type']];
682
+
683
+ if ($el['complex']) {
684
+ $namespace = $this->namespaces[$_argtype['namespace']];
685
+ // XXX need to wrap the parameters in a
686
+ // SOAP_Value.
687
+ }
688
+ if (isset($el['elements'])) {
689
+ foreach ($el['elements'] as $elname => $elattrs) {
690
+ $elname = $this->_sanitize($elname);
691
+ // Is the element a complex type?
692
+ if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
693
+ $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
694
+ } else {
695
+ $this->_addArg($args, $argarray, $elname);
696
+ }
697
+ }
698
+ }
699
+ if ($el['complex'] && $argarray) {
700
+ $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name'];
701
+ $comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
702
+ $argarray = "'{$el['name']}' => \${$el['name']}";
703
+ }
704
+ } else {
705
+ if (isset($_argtype['element'])) {
706
+ // Element argument.
707
+ $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']);
708
+ } else {
709
+ // Complex type argument.
710
+ $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname);
711
+ }
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ // Validate entries.
718
+
719
+ // Operation names are function names, so try to make sure it's
720
+ // legal. This could potentially cause collisions, but let's try
721
+ // to make everything callable and see how many problems that
722
+ // causes.
723
+ $opname_php = $this->_sanitize($opname);
724
+ if (!$this->_validateString($opname_php)) {
725
+ return null;
726
+ }
727
+
728
+ if ($argarray) {
729
+ $argarray = "array($argarray)";
730
+ } else {
731
+ $argarray = 'null';
732
+ }
733
+
734
+ $class .= " function &$opname_php($args)\n {\n$comments$wrappers" .
735
+ " \$result = \$this->call('$opname',\n" .
736
+ " \$v = $argarray,\n" .
737
+ " array('namespace' => '$namespace',\n" .
738
+ " 'soapaction' => '$soapaction',\n" .
739
+ " 'style' => '$opstyle',\n" .
740
+ " 'use' => '$use'" .
741
+ ($this->trace ? ",\n 'trace' => true" : '') . "));\n" .
742
+ " return \$result;\n" .
743
+ " }\n";
744
+ }
745
+
746
+ $class .= "}\n";
747
+
748
+ return $class;
749
+ }
750
+
751
+ function generateAllProxies()
752
+ {
753
+ $proxycode = '';
754
+ foreach (array_keys($this->services[$this->service]['ports']) as $key) {
755
+ $port =& $this->services[$this->service]['ports'][$key];
756
+ $proxycode .= $this->generateProxyCode($port);
757
+ }
758
+ return $proxycode;
759
+ }
760
+
761
+ function &getProxy($port = '', $name = '')
762
+ {
763
+ if ($this->_isfault()) {
764
+ $fault =& $this->_getfault();
765
+ return $fault;
766
+ }
767
+
768
+ $multiport = count($this->services[$this->service]['ports']) > 1;
769
+
770
+ if (!$port) {
771
+ reset($this->services[$this->service]['ports']);
772
+ $port = current($this->services[$this->service]['ports']);
773
+ }
774
+
775
+ if ($multiport || $port) {
776
+ $classname = 'WebService_' . $this->service . '_' . $port['name'];
777
+ } else {
778
+ $classname = 'WebService_' . $this->service;
779
+ }
780
+
781
+ if ($name) {
782
+ $classname = $name . '_' . $classname;
783
+ }
784
+
785
+ $classname = $this->_sanitize($classname);
786
+ if (!class_exists($classname)) {
787
+ $proxy = $this->generateProxyCode($port, $classname);
788
+ require_once 'SOAP/Client.php';
789
+ eval($proxy);
790
+ }
791
+ $proxy = new $classname;
792
+
793
+ return $proxy;
794
+ }
795
+
796
+ /**
797
+ * Sanitizes a SOAP value, method or class name so that it can be used as
798
+ * a valid PHP identifier. Invalid characters are converted into
799
+ * underscores and reserved words are prefixed with an underscore.
800
+ *
801
+ * @param string $name The identifier to sanitize.
802
+ *
803
+ * @return string The sanitized identifier.
804
+ */
805
+ function _sanitize($name)
806
+ {
807
+ $name = preg_replace($this->_invalid, '_', $name);
808
+ if (in_array($name, $this->_reserved)) {
809
+ $name = '_' . $name;
810
+ }
811
+ return $name;
812
+ }
813
+
814
+ function &_getComplexTypeForElement($name, $namespace)
815
+ {
816
+ $t = null;
817
+ if (isset($this->ns[$namespace]) &&
818
+ isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
819
+
820
+ $type = $this->elements[$this->ns[$namespace]][$name]['type'];
821
+ $ns = $this->elements[$this->ns[$namespace]][$name]['namespace'];
822
+
823
+ if (isset($this->complexTypes[$ns][$type])) {
824
+ $t = $this->complexTypes[$ns][$type];
825
+ }
826
+ }
827
+ return $t;
828
+ }
829
+
830
+ function getComplexTypeNameForElement($name, $namespace)
831
+ {
832
+ $t = $this->_getComplexTypeForElement($name, $namespace);
833
+ if ($t) {
834
+ return $t['name'];
835
+ }
836
+ return null;
837
+ }
838
+
839
+ function getComplexTypeChildType($ns, $name, $child_ns, $child_name)
840
+ {
841
+ // Is the type an element?
842
+ $t = $this->_getComplexTypeForElement($name, $ns);
843
+ if ($t) {
844
+ // No, get it from complex types directly.
845
+ if (isset($t['elements'][$child_name]['type']))
846
+ return $t['elements'][$child_name]['type'];
847
+ } elseif (isset($this->ns[$ns]) &&
848
+ isset($this->elements[$this->ns[$ns]][$name]['complex']) &&
849
+ $this->elements[$this->ns[$ns]][$name]['complex']) {
850
+ // Type is not an element but complex.
851
+ return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
852
+ }
853
+ return null;
854
+ }
855
+
856
+ /**
857
+ * @param QName $name A parameter name.
858
+ * @param QName $type A parameter type.
859
+ *
860
+ * @return array A list of [type, array element type, array element
861
+ * namespace, array length].
862
+ */
863
+ function getSchemaType($type, $name)
864
+ {
865
+ // see if it's a complex type so we can deal properly with
866
+ // SOAPENC:arrayType.
867
+ if ($name && $type) {
868
+ // XXX TODO:
869
+ // look up the name in the wsdl and validate the type.
870
+ foreach ($this->complexTypes as $types) {
871
+ if (isset($types[$type->name])) {
872
+ if (isset($types[$type->name]['type'])) {
873
+ list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type->name]['arrayType'])
874
+ ? $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType'])
875
+ : array($this->namespaces[$types[$type->name]['namespace']], null, 0);
876
+ return array($types[$type->name]['type'], $arraytype, $arraytype_ns, $array_depth);
877
+ }
878
+ if (isset($types[$type->name]['arrayType'])) {
879
+ list($arraytype_ns, $arraytype, $array_depth) =
880
+ $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']);
881
+ return array('Array', $arraytype, $arraytype_ns, $array_depth);
882
+ }
883
+ if (!empty($types[$type->name]['elements'][$name->name])) {
884
+ $type->name = $types[$type->name]['elements']['type'];
885
+ return array($type->name, null, $this->namespaces[$types[$type->name]['namespace']], null);
886
+ }
887
+ break;
888
+ }
889
+ }
890
+ }
891
+ if ($type && $type->namespace) {
892
+ $arrayType = null;
893
+ // XXX TODO:
894
+ // this code currently handles only one way of encoding array
895
+ // types in wsdl need to do a generalized function to figure out
896
+ // complex types
897
+ $p = $this->ns[$type->namespace];
898
+ if ($p && !empty($this->complexTypes[$p][$type->name])) {
899
+ if ($arrayType = $this->complexTypes[$p][$type->name]['arrayType']) {
900
+ $type->name = 'Array';
901
+ } elseif ($this->complexTypes[$p][$type->name]['order'] == 'sequence' &&
902
+ array_key_exists('elements', $this->complexTypes[$p][$type->name])) {
903
+ reset($this->complexTypes[$p][$type->name]['elements']);
904
+ // assume an array
905
+ if (count($this->complexTypes[$p][$type->name]['elements']) == 1) {
906
+ $arg = current($this->complexTypes[$p][$type->name]['elements']);
907
+ $arrayType = $arg['type'];
908
+ $type->name = 'Array';
909
+ } else {
910
+ foreach ($this->complexTypes[$p][$type->name]['elements'] as $element) {
911
+ if ($element['name'] == $type->name) {
912
+ $arrayType = $element['type'];
913
+ $type->name = $element['type'];
914
+ }
915
+ }
916
+ }
917
+ } else {
918
+ $type->name = 'Struct';
919
+ }
920
+ return array($type->name, $arrayType, $type->namespace, null);
921
+ }
922
+ }
923
+ return array(null, null, null, null);
924
+ }
925
+
926
+ /**
927
+ * Recurse through the WSDL structure looking for the innermost array type
928
+ * of multi-dimensional arrays.
929
+ *
930
+ * Takes a namespace prefix and a type, which can be in the form 'type' or
931
+ * 'type[]', and returns the full namespace URI, the type of the most
932
+ * deeply nested array type found, and the number of levels of nesting.
933
+ *
934
+ * @access private
935
+ * @return mixed array or nothing
936
+ */
937
+ function _getDeepestArrayType($nsPrefix, $arrayType)
938
+ {
939
+ static $trail = array();
940
+
941
+ $arrayType = ereg_replace('\[\]$', '', $arrayType);
942
+
943
+ // Protect against circular references XXX We really need to remove
944
+ // trail from this altogether (it's very inefficient and in the wrong
945
+ // place!) and put circular reference checking in when the WSDL info
946
+ // is generated in the first place
947
+ if (array_search($nsPrefix . ':' . $arrayType, $trail)) {
948
+ return array(null, null, -count($trail));
949
+ }
950
+
951
+ if (array_key_exists($nsPrefix, $this->complexTypes) &&
952
+ array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) &&
953
+ array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) {
954
+ $trail[] = $nsPrefix . ':' . $arrayType;
955
+ $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
956
+ $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
957
+ return array($result[0], $result[1], $result[2] + 1);
958
+ }
959
+ return array($this->namespaces[$nsPrefix], $arrayType, 0);
960
+ }
961
+
962
+ }
963
+
964
+ class SOAP_WSDL_Cache extends SOAP_Base
965
+ {
966
+ /**
967
+ * Use WSDL cache?
968
+ *
969
+ * @var boolean
970
+ */
971
+ var $_cacheUse;
972
+
973
+ /**
974
+ * WSDL cache directory.
975
+ *
976
+ * @var string
977
+ */
978
+ var $_cacheDir;
979
+
980
+ /**
981
+ * Cache maximum lifetime (in seconds)
982
+ *
983
+ * @var integer
984
+ */
985
+ var $_cacheMaxAge;
986
+
987
+ /**
988
+ * Constructor.
989
+ *
990
+ * @param boolean $cashUse Use caching?
991
+ * @param integer $cacheMaxAge Cache maximum lifetime (in seconds)
992
+ */
993
+ function SOAP_WSDL_Cache($cacheUse = false,
994
+ $cacheMaxAge = WSDL_CACHE_MAX_AGE,
995
+ $cacheDir = null)
996
+ {
997
+ parent::SOAP_Base('WSDLCACHE');
998
+ $this->_cacheUse = $cacheUse;
999
+ $this->_cacheDir = $cacheDir;
1000
+ $this->_cacheMaxAge = $cacheMaxAge;
1001
+ }
1002
+
1003
+ /**
1004
+ * Returns the path to the cache and creates it, if it doesn't exist.
1005
+ *
1006
+ * @private
1007
+ *
1008
+ * @return string The directory to use for the cache.
1009
+ */
1010
+ function _cacheDir()
1011
+ {
1012
+ if (!empty($this->_cacheDir)) {
1013
+ $dir = $this->_cacheDir;
1014
+ } else {
1015
+ $dir = getenv('WSDLCACHE');
1016
+ if (empty($dir)) {
1017
+ $dir = './wsdlcache';
1018
+ }
1019
+ }
1020
+ @mkdir($dir, 0700);
1021
+ return $dir;
1022
+ }
1023
+
1024
+ /**
1025
+ * Retrieves a file from cache if it exists, otherwise retreive from net,
1026
+ * add to cache, and return from cache.
1027
+ *
1028
+ * @param string URL to WSDL
1029
+ * @param array proxy parameters
1030
+ * @param int expected MD5 of WSDL URL
1031
+ * @access public
1032
+ * @return string data
1033
+ */
1034
+ function get($wsdl_fname, $proxy_params = array(), $cache = 0)
1035
+ {
1036
+ $cachename = $md5_wsdl = $file_data = '';
1037
+ if ($this->_cacheUse) {
1038
+ // Try to retrieve WSDL from cache
1039
+ $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl';
1040
+ if (file_exists($cachename) &&
1041
+ $file_data = file_get_contents($cachename)) {
1042
+ $md5_wsdl = md5($file_data);
1043
+ if ($cache) {
1044
+ if ($cache != $md5_wsdl) {
1045
+ return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
1046
+ }
1047
+ } else {
1048
+ $fi = stat($cachename);
1049
+ $cache_mtime = $fi[8];
1050
+ if ($cache_mtime + $this->_cacheMaxAge < time()) {
1051
+ // Expired, refetch.
1052
+ $md5_wsdl = '';
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ // Not cached or not using cache. Retrieve WSDL from URL
1059
+ if (!$md5_wsdl) {
1060
+ // Is it a local file?
1061
+ if (strpos($wsdl_fname, 'file://') === 0) {
1062
+ $wsdl_fname = substr($wsdl_fname, 7);
1063
+ if (!file_exists($wsdl_fname)) {
1064
+ return $this->_raiseSoapFault('Unable to read local WSDL file', $wsdl_fname);
1065
+ }
1066
+ $file_data = file_get_contents($wsdl_fname);
1067
+ } elseif (!preg_match('|^https?://|', $wsdl_fname)) {
1068
+ return $this->_raiseSoapFault('Unknown schema of WSDL URL', $wsdl_fname);
1069
+ } else {
1070
+ $uri = explode('?', $wsdl_fname);
1071
+ $rq = new HTTP_Request($uri[0], $proxy_params);
1072
+ // the user agent HTTP_Request uses fouls things up
1073
+ if (isset($uri[1])) {
1074
+ $rq->addRawQueryString($uri[1]);
1075
+ }
1076
+
1077
+ if (isset($proxy_params['proxy_host']) &&
1078
+ isset($proxy_params['proxy_port']) &&
1079
+ isset($proxy_params['proxy_user']) &&
1080
+ isset($proxy_params['proxy_pass'])) {
1081
+ $rq->setProxy($proxy_params['proxy_host'],
1082
+ $proxy_params['proxy_port'],
1083
+ $proxy_params['proxy_user'],
1084
+ $proxy_params['proxy_pass']);
1085
+ } elseif (isset($proxy_params['proxy_host']) &&
1086
+ isset($proxy_params['proxy_port'])) {
1087
+ $rq->setProxy($proxy_params['proxy_host'],
1088
+ $proxy_params['proxy_port']);
1089
+ }
1090
+
1091
+ $result = $rq->sendRequest();
1092
+ if (PEAR::isError($result)) {
1093
+ return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname);
1094
+ }
1095
+ $file_data = $rq->getResponseBody();
1096
+ if (!$file_data) {
1097
+ return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname);
1098
+ }
1099
+ }
1100
+
1101
+ $md5_wsdl = md5($file_data);
1102
+
1103
+ if ($this->_cacheUse) {
1104
+ $fp = fopen($cachename, "wb");
1105
+ fwrite($fp, $file_data);
1106
+ fclose($fp);
1107
+ }
1108
+ }
1109
+
1110
+ if ($this->_cacheUse && $cache && $cache != $md5_wsdl) {
1111
+ return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname);
1112
+ }
1113
+
1114
+ return $file_data;
1115
+ }
1116
+
1117
+ }
1118
+
1119
+ class SOAP_WSDL_Parser extends SOAP_Base
1120
+ {
1121
+
1122
+ /**
1123
+ * Define internal arrays of bindings, ports, operations,
1124
+ * messages, etc.
1125
+ */
1126
+ var $currentMessage;
1127
+ var $currentOperation;
1128
+ var $currentPortType;
1129
+ var $currentBinding;
1130
+ var $currentPort;
1131
+
1132
+ /**
1133
+ * Parser vars.
1134
+ */
1135
+ var $cache;
1136
+
1137
+ var $tns = null;
1138
+ var $soapns = array('soap');
1139
+ var $uri = '';
1140
+ var $wsdl = null;
1141
+
1142
+ var $status = '';
1143
+ var $element_stack = array();
1144
+ var $parentElement = '';
1145
+
1146
+ var $schema = '';
1147
+ var $schemaStatus = '';
1148
+ var $schema_stack = array();
1149
+ var $currentComplexType;
1150
+ var $schema_element_stack = array();
1151
+ var $currentElement;
1152
+
1153
+ /**
1154
+ * Constructor.
1155
+ */
1156
+ function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false)
1157
+ {
1158
+ parent::SOAP_Base('WSDLPARSER');
1159
+ $this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse,
1160
+ $wsdl->cacheMaxAge,
1161
+ $wsdl->cacheDir);
1162
+ $this->uri = $uri;
1163
+ $this->wsdl = &$wsdl;
1164
+ $this->docs = $docs;
1165
+ $this->parse($uri);
1166
+ }
1167
+
1168
+ function parse($uri)
1169
+ {
1170
+ // Check whether content has been read.
1171
+ $fd = $this->cache->get($uri, $this->wsdl->proxy);
1172
+ if (PEAR::isError($fd)) {
1173
+ return $this->_raiseSoapFault($fd);
1174
+ }
1175
+
1176
+ // Create an XML parser.
1177
+ $parser = xml_parser_create();
1178
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
1179
+ xml_set_object($parser, $this);
1180
+ xml_set_element_handler($parser, 'startElement', 'endElement');
1181
+ if ($this->docs) {
1182
+ xml_set_character_data_handler($parser, 'characterData');
1183
+ }
1184
+
1185
+ if (!xml_parse($parser, $fd, true)) {
1186
+ $detail = sprintf('XML error on line %d: %s',
1187
+ xml_get_current_line_number($parser),
1188
+ xml_error_string(xml_get_error_code($parser)));
1189
+ return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
1190
+ }
1191
+ xml_parser_free($parser);
1192
+ return true;
1193
+ }
1194
+
1195
+ /**
1196
+ * start-element handler
1197
+ */
1198
+ function startElement($parser, $name, $attrs)
1199
+ {
1200
+ // Get element prefix.
1201
+ $qname = new QName($name);
1202
+ if ($qname->ns) {
1203
+ $ns = $qname->ns;
1204
+ if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) {
1205
+ $name = $qname->name;
1206
+ }
1207
+ }
1208
+ $this->currentTag = $qname->name;
1209
+ $this->parentElement = '';
1210
+ $stack_size = count($this->element_stack);
1211
+ if ($stack_size) {
1212
+ $this->parentElement = $this->element_stack[$stack_size - 1];
1213
+ }
1214
+ $this->element_stack[] = $this->currentTag;
1215
+
1216
+ // Find status, register data.
1217
+ switch ($this->status) {
1218
+ case 'types':
1219
+ // sect 2.2 wsdl:types
1220
+ // children: xsd:schema
1221
+ $parent_tag = '';
1222
+ $stack_size = count($this->schema_stack);
1223
+ if ($stack_size) {
1224
+ $parent_tag = $this->schema_stack[$stack_size - 1];
1225
+ }
1226
+
1227
+ switch ($qname->name) {
1228
+ case 'schema':
1229
+ // No parent should be in the stack.
1230
+ if (!$parent_tag || $parent_tag == 'types') {
1231
+ if (array_key_exists('targetNamespace', $attrs)) {
1232
+ $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
1233
+ } else {
1234
+ $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
1235
+ }
1236
+ $this->wsdl->complexTypes[$this->schema] = array();
1237
+ $this->wsdl->elements[$this->schema] = array();
1238
+ }
1239
+ break;
1240
+
1241
+ case 'complexType':
1242
+ if ($parent_tag == 'schema') {
1243
+ $this->currentComplexType = $attrs['name'];
1244
+ if (!isset($attrs['namespace'])) {
1245
+ $attrs['namespace'] = $this->schema;
1246
+ }
1247
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs;
1248
+ if (array_key_exists('base', $attrs)) {
1249
+ $qn = new QName($attrs['base']);
1250
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
1251
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns;
1252
+ } else {
1253
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
1254
+ }
1255
+ $this->schemaStatus = 'complexType';
1256
+ } else {
1257
+ $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true;
1258
+ }
1259
+ break;
1260
+
1261
+ case 'element':
1262
+ if (isset($attrs['type'])) {
1263
+ $qn = new QName($attrs['type']);
1264
+ $attrs['type'] = $qn->name;
1265
+ if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) {
1266
+ $attrs['namespace'] = $qn->ns;
1267
+ }
1268
+ }
1269
+
1270
+ $parentElement = '';
1271
+ $stack_size = count($this->schema_element_stack);
1272
+ if ($stack_size > 0) {
1273
+ $parentElement = $this->schema_element_stack[$stack_size - 1];
1274
+ }
1275
+
1276
+ if (isset($attrs['ref'])) {
1277
+ $qn = new QName($attrs['ref']);
1278
+ $this->currentElement = $qn->name;
1279
+ } else {
1280
+ $this->currentElement = $attrs['name'];
1281
+ }
1282
+ $this->schema_element_stack[] = $this->currentElement;
1283
+ if (!isset($attrs['namespace'])) {
1284
+ $attrs['namespace'] = $this->schema;
1285
+ }
1286
+
1287
+ if ($parent_tag == 'schema') {
1288
+ $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs;
1289
+ $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false;
1290
+ $this->schemaStatus = 'element';
1291
+ } elseif ($this->currentComplexType) {
1292
+ // we're inside a complexType
1293
+ if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) &&
1294
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence')
1295
+ && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') {
1296
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null;
1297
+ }
1298
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs;
1299
+ } else {
1300
+ $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs;
1301
+ }
1302
+ break;
1303
+
1304
+ case 'complexContent':
1305
+ case 'simpleContent':
1306
+ break;
1307
+
1308
+ case 'extension':
1309
+ case 'restriction':
1310
+ if ($this->schemaStatus == 'complexType') {
1311
+ if (!empty($attrs['base'])) {
1312
+ $qn = new QName($attrs['base']);
1313
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name;
1314
+
1315
+ // Types that extend from other types aren't
1316
+ // *of* those types. Reflect this by denoting
1317
+ // which type they extend. I'm leaving the
1318
+ // 'type' setting here since I'm not sure what
1319
+ // removing it might break at the moment.
1320
+ if ($qname->name == 'extension') {
1321
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name;
1322
+ }
1323
+ } else {
1324
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
1325
+ }
1326
+ }
1327
+ break;
1328
+
1329
+ case 'sequence':
1330
+ if ($this->schemaStatus == 'complexType') {
1331
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
1332
+ if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
1333
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
1334
+ }
1335
+ }
1336
+ break;
1337
+
1338
+ case 'all':
1339
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
1340
+ if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
1341
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
1342
+ }
1343
+ break;
1344
+
1345
+ case 'choice':
1346
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name;
1347
+ if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
1348
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
1349
+ }
1350
+
1351
+ case 'attribute':
1352
+ if ($this->schemaStatus == 'complexType') {
1353
+ if (isset($attrs['name'])) {
1354
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs;
1355
+ } else {
1356
+ if (isset($attrs['ref'])) {
1357
+ $q = new QName($attrs['ref']);
1358
+ foreach ($attrs as $k => $v) {
1359
+ if ($k != 'ref' && strstr($k, $q->name)) {
1360
+ $vq = new QName($v);
1361
+ if ($q->name == 'arrayType') {
1362
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo;
1363
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array';
1364
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns;
1365
+ } else {
1366
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name;
1367
+ }
1368
+ }
1369
+ }
1370
+ }
1371
+ }
1372
+ }
1373
+ break;
1374
+ }
1375
+
1376
+ $this->schema_stack[] = $qname->name;
1377
+ break;
1378
+
1379
+ case 'message':
1380
+ // sect 2.3 wsdl:message child wsdl:part
1381
+ switch ($qname->name) {
1382
+ case 'part':
1383
+ $qn = null;
1384
+ if (isset($attrs['type'])) {
1385
+ $qn = new QName($attrs['type']);
1386
+ } elseif (isset($attrs['element'])) {
1387
+ $qn = new QName($attrs['element']);
1388
+ }
1389
+ if ($qn) {
1390
+ $attrs['type'] = $qn->name;
1391
+ $attrs['namespace'] = $qn->ns;
1392
+ }
1393
+ $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs;
1394
+ // error in wsdl
1395
+
1396
+ case 'documentation':
1397
+ break;
1398
+
1399
+ default:
1400
+ break;
1401
+ }
1402
+ break;
1403
+
1404
+ case 'portType':
1405
+ // sect 2.4
1406
+ switch ($qname->name) {
1407
+ case 'operation':
1408
+ // attributes: name
1409
+ // children: wsdl:input wsdl:output wsdl:fault
1410
+ $this->currentOperation = $attrs['name'];
1411
+ $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs;
1412
+ break;
1413
+
1414
+ case 'input':
1415
+ case 'output':
1416
+ case 'fault':
1417
+ // wsdl:input wsdl:output wsdl:fault
1418
+ // attributes: name message parameterOrder(optional)
1419
+ if ($this->currentOperation) {
1420
+ if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
1421
+ $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs);
1422
+ } else {
1423
+ $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs;
1424
+ }
1425
+ if (array_key_exists('message', $attrs)) {
1426
+ $qn = new QName($attrs['message']);
1427
+ $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name;
1428
+ $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns;
1429
+ }
1430
+ }
1431
+ break;
1432
+
1433
+ case 'documentation':
1434
+ break;
1435
+
1436
+ default:
1437
+ break;
1438
+ }
1439
+ break;
1440
+
1441
+ case 'binding':
1442
+ $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
1443
+ switch ($ns) {
1444
+ case SCHEMA_SOAP:
1445
+ case SCHEMA_SOAP12:
1446
+ // this deals with wsdl section 3 soap binding
1447
+ switch ($qname->name) {
1448
+ case 'binding':
1449
+ // sect 3.3
1450
+ // soap:binding, attributes: transport(required), style(optional, default = document)
1451
+ // if style is missing, it is assumed to be 'document'
1452
+ if (!isset($attrs['style'])) {
1453
+ $attrs['style'] = 'document';
1454
+ }
1455
+ $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
1456
+ break;
1457
+
1458
+ case 'operation':
1459
+ // sect 3.4
1460
+ // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
1461
+ if (!isset($attrs['style'])) {
1462
+ $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style'];
1463
+ }
1464
+ if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) {
1465
+ $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs);
1466
+ } else {
1467
+ $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs;
1468
+ }
1469
+ break;
1470
+
1471
+ case 'body':
1472
+ // sect 3.5
1473
+ // soap:body attributes:
1474
+ // part - optional. listed parts must appear in body, missing means all parts appear in body
1475
+ // use - required. encoded|literal
1476
+ // encodingStyle - optional. space seperated list of encodings (uri's)
1477
+ $this->wsdl->bindings[$this->currentBinding]
1478
+ ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
1479
+ break;
1480
+
1481
+ case 'fault':
1482
+ // sect 3.6
1483
+ // soap:fault attributes: name use encodingStyle namespace
1484
+ $this->wsdl->bindings[$this->currentBinding]
1485
+ ['operations'][$this->currentOperation][$this->opStatus] = $attrs;
1486
+ break;
1487
+
1488
+ case 'header':
1489
+ // sect 3.7
1490
+ // soap:header attributes: message part use encodingStyle namespace
1491
+ $this->wsdl->bindings[$this->currentBinding]
1492
+ ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
1493
+ break;
1494
+
1495
+ case 'headerfault':
1496
+ // sect 3.7
1497
+ // soap:header attributes: message part use encodingStyle namespace
1498
+ $header = count($this->wsdl->bindings[$this->currentBinding]
1499
+ ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
1500
+ $this->wsdl->bindings[$this->currentBinding]
1501
+ ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs;
1502
+ break;
1503
+
1504
+ case 'documentation':
1505
+ break;
1506
+
1507
+ default:
1508
+ // error! not a valid element inside binding
1509
+ break;
1510
+ }
1511
+ break;
1512
+
1513
+ case SCHEMA_WSDL:
1514
+ // XXX verify correct namespace
1515
+ // for now, default is the 'wsdl' namespace
1516
+ // other possible namespaces include smtp, http, etc. for alternate bindings
1517
+ switch ($qname->name) {
1518
+ case 'operation':
1519
+ // sect 2.5
1520
+ // wsdl:operation attributes: name
1521
+ $this->currentOperation = $attrs['name'];
1522
+ break;
1523
+
1524
+ case 'output':
1525
+ case 'input':
1526
+ case 'fault':
1527
+ // sect 2.5
1528
+ // wsdl:input attributes: name
1529
+ $this->opStatus = $qname->name;
1530
+ break;
1531
+
1532
+ case 'documentation':
1533
+ break;
1534
+
1535
+ default:
1536
+ break;
1537
+ }
1538
+ break;
1539
+
1540
+ case SCHEMA_WSDL_HTTP:
1541
+ switch ($qname->name) {
1542
+ case 'binding':
1543
+ // sect 4.4
1544
+ // http:binding attributes: verb
1545
+ // parent: wsdl:binding
1546
+ $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs);
1547
+ break;
1548
+
1549
+ case 'operation':
1550
+ // sect 4.5
1551
+ // http:operation attributes: location
1552
+ // parent: wsdl:operation
1553
+ $this->wsdl->bindings[$this->currentBinding]['operations']
1554
+ [$this->currentOperation] = $attrs;
1555
+ break;
1556
+
1557
+ case 'urlEncoded':
1558
+ // sect 4.6
1559
+ // http:urlEncoded attributes: location
1560
+ // parent: wsdl:input wsdl:output etc.
1561
+ $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
1562
+ [$this->currentOperation]['uri'] = 'urlEncoded';
1563
+ break;
1564
+
1565
+ case 'urlReplacement':
1566
+ // sect 4.7
1567
+ // http:urlReplacement attributes: location
1568
+ // parent: wsdl:input wsdl:output etc.
1569
+ $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
1570
+ [$this->currentOperation]['uri'] = 'urlReplacement';
1571
+ break;
1572
+
1573
+ case 'documentation':
1574
+ break;
1575
+
1576
+ default:
1577
+ // error
1578
+ break;
1579
+ }
1580
+
1581
+ case SCHEMA_MIME:
1582
+ // sect 5
1583
+ // all mime parts are children of wsdl:input, wsdl:output, etc.
1584
+ // unsuported as of yet
1585
+ switch ($qname->name) {
1586
+ case 'content':
1587
+ // sect 5.3 mime:content
1588
+ // <mime:content part="nmtoken"? type="string"?/>
1589
+ // part attribute only required if content is child of multipart related,
1590
+ // it contains the name of the part
1591
+ // type attribute contains the mime type
1592
+ case 'multipartRelated':
1593
+ // sect 5.4 mime:multipartRelated
1594
+ case 'part':
1595
+ case 'mimeXml':
1596
+ // sect 5.6 mime:mimeXml
1597
+ // <mime:mimeXml part="nmtoken"?/>
1598
+ //
1599
+ case 'documentation':
1600
+ break;
1601
+
1602
+ default:
1603
+ // error
1604
+ break;
1605
+ }
1606
+
1607
+ case SCHEMA_DIME:
1608
+ // DIME is defined in:
1609
+ // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
1610
+ // all DIME parts are children of wsdl:input, wsdl:output, etc.
1611
+ // unsuported as of yet
1612
+ switch ($qname->name) {
1613
+ case 'message':
1614
+ // sect 4.1 dime:message
1615
+ // appears in binding section
1616
+ $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs;
1617
+ break;
1618
+
1619
+ default:
1620
+ break;
1621
+ }
1622
+
1623
+ default:
1624
+ break;
1625
+ }
1626
+ break;
1627
+
1628
+ case 'service':
1629
+ $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
1630
+
1631
+ switch ($qname->name) {
1632
+ case 'port':
1633
+ // sect 2.6 wsdl:port attributes: name binding
1634
+ $this->currentPort = $attrs['name'];
1635
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs;
1636
+ // XXX hack to deal with binding namespaces
1637
+ $qn = new QName($attrs['binding']);
1638
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name;
1639
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns;
1640
+ break;
1641
+
1642
+ case 'address':
1643
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs;
1644
+ // what TYPE of port is it? SOAP or HTTP?
1645
+ $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL;
1646
+ switch ($ns) {
1647
+ case SCHEMA_WSDL_HTTP:
1648
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
1649
+ break;
1650
+
1651
+ case SCHEMA_SOAP:
1652
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
1653
+ break;
1654
+
1655
+ default:
1656
+ // Shouldn't happen, we'll assume SOAP.
1657
+ $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
1658
+ }
1659
+
1660
+ break;
1661
+
1662
+ case 'documentation':
1663
+ break;
1664
+
1665
+ default:
1666
+ break;
1667
+ }
1668
+ }
1669
+
1670
+ // Top level elements found under wsdl:definitions.
1671
+ switch ($qname->name) {
1672
+ case 'import':
1673
+ // sect 2.1.1 wsdl:import attributes: namespace location
1674
+ if ((isset($attrs['location']) || isset($attrs['schemaLocation'])) &&
1675
+ !isset($this->wsdl->imports[$attrs['namespace']])) {
1676
+ $uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation'];
1677
+ $location = @parse_url($uri);
1678
+ if (!isset($location['scheme'])) {
1679
+ $base = @parse_url($this->uri);
1680
+ $uri = $this->mergeUrl($base, $uri);
1681
+ }
1682
+
1683
+ $this->wsdl->imports[$attrs['namespace']] = $attrs;
1684
+ $import_parser_class = get_class($this);
1685
+ $import_parser = new $import_parser_class($uri, $this->wsdl, $this->docs);
1686
+ if ($import_parser->fault) {
1687
+ unset($this->wsdl->imports[$attrs['namespace']]);
1688
+ return false;
1689
+ }
1690
+ $this->currentImport = $attrs['namespace'];
1691
+ }
1692
+ // Continue on to the 'types' case - lack of break; is
1693
+ // intentional.
1694
+
1695
+ case 'types':
1696
+ // sect 2.2 wsdl:types
1697
+ $this->status = 'types';
1698
+ break;
1699
+
1700
+ case 'schema':
1701
+ // We can hit this at the top level if we've been asked to
1702
+ // import an XSD file.
1703
+ if (!empty($attrs['targetNamespace'])) {
1704
+ $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
1705
+ } else {
1706
+ $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
1707
+ }
1708
+ $this->wsdl->complexTypes[$this->schema] = array();
1709
+ $this->wsdl->elements[$this->schema] = array();
1710
+ $this->schema_stack[] = $qname->name;
1711
+ $this->status = 'types';
1712
+ break;
1713
+
1714
+ case 'message':
1715
+ // sect 2.3 wsdl:message attributes: name children:wsdl:part
1716
+ $this->status = 'message';
1717
+ if (isset($attrs['name'])) {
1718
+ $this->currentMessage = $attrs['name'];
1719
+ $this->wsdl->messages[$this->currentMessage] = array();
1720
+ }
1721
+ break;
1722
+
1723
+ case 'portType':
1724
+ // sect 2.4 wsdl:portType
1725
+ // attributes: name
1726
+ // children: wsdl:operation
1727
+ $this->status = 'portType';
1728
+ $this->currentPortType = $attrs['name'];
1729
+ $this->wsdl->portTypes[$this->currentPortType] = array();
1730
+ break;
1731
+
1732
+ case 'binding':
1733
+ // sect 2.5 wsdl:binding attributes: name type
1734
+ // children: wsdl:operation soap:binding http:binding
1735
+ if ($qname->ns && $qname->ns != $this->tns) {
1736
+ break;
1737
+ }
1738
+ $this->status = 'binding';
1739
+ $this->currentBinding = $attrs['name'];
1740
+ $qn = new QName($attrs['type']);
1741
+ $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name;
1742
+ $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns;
1743
+ break;
1744
+
1745
+ case 'service':
1746
+ // sect 2.7 wsdl:service attributes: name children: ports
1747
+ $this->currentService = $attrs['name'];
1748
+ $this->wsdl->services[$this->currentService]['ports'] = array();
1749
+ $this->status = 'service';
1750
+ break;
1751
+
1752
+ case 'definitions':
1753
+ // sec 2.1 wsdl:definitions
1754
+ // attributes: name targetNamespace xmlns:*
1755
+ // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
1756
+ $this->wsdl->definition = $attrs;
1757
+ foreach ($attrs as $key => $value) {
1758
+ if (strstr($key, 'xmlns:') !== false) {
1759
+ $qn = new QName($key);
1760
+ // XXX need to refactor ns handling.
1761
+ $this->wsdl->namespaces[$qn->name] = $value;
1762
+ $this->wsdl->ns[$value] = $qn->name;
1763
+ if ($key == 'targetNamespace' ||
1764
+ strcasecmp($value,SOAP_SCHEMA) == 0) {
1765
+ $this->soapns[] = $qn->name;
1766
+ } else {
1767
+ if (in_array($value, $this->_XMLSchema)) {
1768
+ $this->wsdl->xsd = $value;
1769
+ }
1770
+ }
1771
+ }
1772
+ }
1773
+ if (isset($ns) && $ns) {
1774
+ $namespace = 'xmlns:' . $ns;
1775
+ if (!$this->wsdl->definition[$namespace]) {
1776
+ return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri);
1777
+ }
1778
+ $this->tns = $ns;
1779
+ }
1780
+ break;
1781
+ }
1782
+ }
1783
+
1784
+ /**
1785
+ * end-element handler.
1786
+ */
1787
+ function endElement($parser, $name)
1788
+ {
1789
+ $stacksize = count($this->element_stack);
1790
+ if ($stacksize) {
1791
+ if ($this->element_stack[$stacksize - 1] == 'definitions') {
1792
+ $this->status = '';
1793
+ }
1794
+ array_pop($this->element_stack);
1795
+ }
1796
+
1797
+ if (stristr($name, 'schema')) {
1798
+ array_pop($this->schema_stack);
1799
+ $this->schema = '';
1800
+ }
1801
+
1802
+ if ($this->schema) {
1803
+ array_pop($this->schema_stack);
1804
+ if (count($this->schema_stack) <= 1) {
1805
+ /* Correct the type for sequences with multiple
1806
+ * elements. */
1807
+ if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
1808
+ && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array'
1809
+ && array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
1810
+ && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) {
1811
+ $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct';
1812
+ }
1813
+ }
1814
+ if (stristr($name, 'complexType')) {
1815
+ $this->currentComplexType = '';
1816
+ if (count($this->schema_element_stack)) {
1817
+ $this->currentElement = array_pop($this->schema_element_stack);
1818
+ } else {
1819
+ $this->currentElement = '';
1820
+ }
1821
+ } elseif (stristr($name, 'element')) {
1822
+ if (count($this->schema_element_stack)) {
1823
+ $this->currentElement = array_pop($this->schema_element_stack);
1824
+ } else {
1825
+ $this->currentElement = '';
1826
+ }
1827
+ }
1828
+ }
1829
+ }
1830
+
1831
+ /**
1832
+ * Element content handler.
1833
+ */
1834
+ function characterData($parser, $data)
1835
+ {
1836
+ // Store the documentation in the WSDL file.
1837
+ if ($this->currentTag == 'documentation') {
1838
+ $data = trim(preg_replace('/\s+/', ' ', $data));
1839
+ if (!strlen($data)) {
1840
+ return;
1841
+ }
1842
+
1843
+ switch ($this->status) {
1844
+ case 'service':
1845
+ $ptr =& $this->wsdl->services[$this->currentService];
1846
+ break;
1847
+
1848
+ case 'portType':
1849
+ $ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation];
1850
+ break;
1851
+
1852
+ case 'binding':
1853
+ $ptr =& $this->wsdl->bindings[$this->currentBinding];
1854
+ break;
1855
+
1856
+ case 'message':
1857
+ $ptr =& $this->wsdl->messages[$this->currentMessage];
1858
+ break;
1859
+
1860
+ case 'operation':
1861
+ break;
1862
+
1863
+ case 'types':
1864
+ if (isset($this->currentComplexType) &&
1865
+ isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
1866
+ if ($this->currentElement) {
1867
+ $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement];
1868
+ } else {
1869
+ $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType];
1870
+ }
1871
+ }
1872
+ break;
1873
+ }
1874
+
1875
+ if (isset($ptr)) {
1876
+ if (!isset($ptr['documentation'])) {
1877
+ $ptr['documentation'] = '';
1878
+ } else {
1879
+ $ptr['documentation'] .= ' ';
1880
+ }
1881
+ $ptr['documentation'] .= $data;
1882
+ }
1883
+ }
1884
+ }
1885
+
1886
+ /**
1887
+ * $parsed is an array returned by parse_url().
1888
+ *
1889
+ * @access private
1890
+ */
1891
+ function mergeUrl($parsed, $path)
1892
+ {
1893
+ if (!is_array($parsed)) {
1894
+ return false;
1895
+ }
1896
+
1897
+ $uri = '';
1898
+ if (!empty($parsed['scheme'])) {
1899
+ $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
1900
+ $uri = $parsed['scheme'] . $sep;
1901
+ }
1902
+
1903
+ if (isset($parsed['pass'])) {
1904
+ $uri .= "$parsed[user]:$parsed[pass]@";
1905
+ } elseif (isset($parsed['user'])) {
1906
+ $uri .= "$parsed[user]@";
1907
+ }
1908
+
1909
+ if (isset($parsed['host'])) {
1910
+ $uri .= $parsed['host'];
1911
+ }
1912
+ if (isset($parsed['port'])) {
1913
+ $uri .= ":$parsed[port]";
1914
+ }
1915
+ if ($path[0] != '/' && isset($parsed['path'])) {
1916
+ if ($parsed['path'][strlen($parsed['path']) - 1] != '/') {
1917
+ $path = dirname($parsed['path']) . '/' . $path;
1918
+ } else {
1919
+ $path = $parsed['path'] . $path;
1920
+ }
1921
+ $path = $this->_normalize($path);
1922
+ }
1923
+ $sep = $path[0] == '/' ? '' : '/';
1924
+ $uri .= $sep . $path;
1925
+
1926
+ return $uri;
1927
+ }
1928
+
1929
+ function _normalize($path_str)
1930
+ {
1931
+ $pwd = '';
1932
+ $strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY);
1933
+ $pwdArr = '';
1934
+ $j = 0;
1935
+ for ($i = 0; $i < count($strArr); $i++) {
1936
+ if ($strArr[$i] != ' ..') {
1937
+ if ($strArr[$i] != ' .') {
1938
+ $pwdArr[$j] = $strArr[$i];
1939
+ $j++;
1940
+ }
1941
+ } else {
1942
+ array_pop($pwdArr);
1943
+ $j--;
1944
+ }
1945
+ }
1946
+ $pStr = implode('/', $pwdArr);
1947
+ $pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/';
1948
+ return $pwd;
1949
+ }
1950
+
1951
+ }
1952
+
1953
+ /**
1954
+ * Parses the types and methods used in web service objects into the internal
1955
+ * data structures used by SOAP_WSDL.
1956
+ *
1957
+ * Assumes the SOAP_WSDL class is unpopulated to start with.
1958
+ *
1959
+ * @author Chris Coe <info@intelligentstreaming.com>
1960
+ */
1961
+ class SOAP_WSDL_ObjectParser extends SOAP_Base
1962
+ {
1963
+ /**
1964
+ * Target namespace for the WSDL document will have the following
1965
+ * prefix.
1966
+ */
1967
+ var $tnsPrefix = 'tns';
1968
+
1969
+ /**
1970
+ * Reference to the SOAP_WSDL object to populate.
1971
+ */
1972
+ var $wsdl = null;
1973
+
1974
+ /**
1975
+ * Constructor.
1976
+ *
1977
+ * @param object|array $objects Reference to the object or array of
1978
+ * objects to parse.
1979
+ * @param SOAP_WSDL $wsdl Reference to the SOAP_WSDL object to
1980
+ * populate.
1981
+ * @param string $targetNamespace The target namespace of schema types
1982
+ * etc.
1983
+ * @param string $service_name Name of the WSDL <service>.
1984
+ * @param string $service_desc Optional description of the WSDL
1985
+ * <service>.
1986
+ */
1987
+ function SOAP_WSDL_ObjectParser($objects, &$wsdl, $targetNamespace,
1988
+ $service_name, $service_desc = '')
1989
+ {
1990
+ parent::SOAP_Base('WSDLOBJECTPARSER');
1991
+
1992
+ $this->wsdl = &$wsdl;
1993
+
1994
+ // Set up the SOAP_WSDL object
1995
+ $this->_initialise($service_name);
1996
+
1997
+ // Parse each web service object
1998
+ $wsdl_ref = is_array($objects) ? $objects : array($objects);
1999
+
2000
+ foreach ($wsdl_ref as $ref_item) {
2001
+ if (!is_object($ref_item)) {
2002
+ $this->_raiseSoapFault('Invalid web service object passed to object parser');
2003
+ continue;
2004
+ }
2005
+
2006
+ if (!$this->_parse($ref_item, $targetNamespace, $service_name)) {
2007
+ break;
2008
+ }
2009
+ }
2010
+
2011
+ // Build bindings from abstract data.
2012
+ if ($this->fault == null) {
2013
+ $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc);
2014
+ }
2015
+ }
2016
+
2017
+ /**
2018
+ * Initialise the SOAP_WSDL tree (destructive).
2019
+ *
2020
+ * If the object has already been initialised, the only effect
2021
+ * will be to change the tns namespace to the new service name.
2022
+ *
2023
+ * @param $service_name Name of the WSDL <service>
2024
+ * @access private
2025
+ */
2026
+ function _initialise($service_name)
2027
+ {
2028
+ // Set up the basic namespaces that all WSDL definitions use.
2029
+ $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL; // WSDL language
2030
+ $this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings
2031
+ $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace
2032
+ $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema
2033
+ $this->wsdl->namespaces[SOAP_BASE::SOAPENCPrefix()] = array_search(SOAP_BASE::SOAPENCPrefix(), $this->_namespaces); // SOAP types
2034
+
2035
+ // XXX Refactor $namespace/$ns for Shane :-)
2036
+ unset($this->wsdl->ns['urn:' . $service_name]);
2037
+ $this->wsdl->ns += array_flip($this->wsdl->namespaces);
2038
+
2039
+ // Imports are not implemented in WSDL generation from classes.
2040
+ // *** <wsdl:import> ***
2041
+ }
2042
+
2043
+ /**
2044
+ * Parser - takes a single object to add to tree (non-destructive).
2045
+ *
2046
+ * @access private
2047
+ *
2048
+ * @param object $object Reference to the object to parse.
2049
+ * @param string $schemaNamespace
2050
+ * @param string $service_name Name of the WSDL <service>.
2051
+ */
2052
+ function _parse($object, $schemaNamespace, $service_name)
2053
+ {
2054
+ // Create namespace prefix for the schema
2055
+ list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}');
2056
+
2057
+ // Parse all the types defined by the object in whatever
2058
+ // schema language we are using (currently __typedef arrays)
2059
+ // *** <wsdl:types> ***
2060
+ foreach ($object->__typedef as $typeName => $typeValue) {
2061
+ // Get/create namespace definition
2062
+ list($nsPrefix, $typeName) = $this->_getTypeNs($typeName);
2063
+
2064
+ // Create type definition
2065
+ $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName);
2066
+ $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName];
2067
+
2068
+ // According to Dmitri's documentation, __typedef comes in two
2069
+ // flavors:
2070
+ // Array = array(array("item" => "value"))
2071
+ // Struct = array("item1" => "value1", "item2" => "value2", ...)
2072
+ if (is_array($typeValue)) {
2073
+ if (is_array(current($typeValue)) && count($typeValue) == 1
2074
+ && count(current($typeValue)) == 1) {
2075
+ // It's an array
2076
+ $thisType['type'] = 'Array';
2077
+ $nsType = current(current($typeValue));
2078
+ list($nsPrefix, $typeName) = $this->_getTypeNs($nsType);
2079
+ $thisType['namespace'] = $nsPrefix;
2080
+ $thisType['arrayType'] = $typeName . '[]';
2081
+ } elseif (!is_array(current($typeValue))) {
2082
+ // It's a struct
2083
+ $thisType['type'] = 'Struct';
2084
+ $thisType['order'] = 'all';
2085
+ $thisType['namespace'] = $nsPrefix;
2086
+ $thisType['elements'] = array();
2087
+
2088
+ foreach ($typeValue as $elementName => $elementType) {
2089
+ list($nsPrefix, $typeName) = $this->_getTypeNs($elementType);
2090
+ $thisType['elements'][$elementName]['name'] = $elementName;
2091
+ $thisType['elements'][$elementName]['type'] = $typeName;
2092
+ $thisType['elements'][$elementName]['namespace'] = $nsPrefix;
2093
+ }
2094
+ } else {
2095
+ // It's erroneous
2096
+ return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
2097
+ }
2098
+ } else {
2099
+ // It's erroneous
2100
+ return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object));
2101
+ }
2102
+ }
2103
+
2104
+ // Create an empty element array with the target namespace
2105
+ // prefix, to match the results of WSDL parsing.
2106
+ $this->wsdl->elements[$schPrefix] = array();
2107
+
2108
+ // Populate tree with message information
2109
+ // *** <wsdl:message> ***
2110
+ foreach ($object->__dispatch_map as $operationName => $messages) {
2111
+ // We need at least 'in' and 'out' parameters.
2112
+ if (!isset($messages['in']) || !isset($messages['out'])) {
2113
+ return $this->_raiseSoapFault('The dispatch map for the method "' . $operationName . '" is missing an "in" or "out" definition.', 'urn:' . get_class($object));
2114
+ }
2115
+ foreach ($messages as $messageType => $messageParts) {
2116
+ unset($thisMessage);
2117
+
2118
+ switch ($messageType) {
2119
+ case 'in':
2120
+ $this->wsdl->messages[$operationName . 'Request'] = array();
2121
+ $thisMessage =& $this->wsdl->messages[$operationName . 'Request'];
2122
+ break;
2123
+
2124
+ case 'out':
2125
+ $this->wsdl->messages[$operationName . 'Response'] = array();
2126
+ $thisMessage =& $this->wsdl->messages[$operationName . 'Response'];
2127
+ break;
2128
+
2129
+ case 'alias':
2130
+ // Do nothing
2131
+ break;
2132
+
2133
+ default:
2134
+ // Error condition
2135
+ break;
2136
+ }
2137
+
2138
+ if (isset($thisMessage)) {
2139
+ foreach ($messageParts as $partName => $partType) {
2140
+ list ($nsPrefix, $typeName) = $this->_getTypeNs($partType);
2141
+
2142
+ $thisMessage[$partName] = array(
2143
+ 'name' => $partName,
2144
+ 'type' => $typeName,
2145
+ 'namespace' => $nsPrefix
2146
+ );
2147
+ }
2148
+ }
2149
+ }
2150
+ }
2151
+
2152
+ // Populate tree with portType information
2153
+ // XXX Current implementation only supports one portType that
2154
+ // encompasses all of the operations available.
2155
+ // *** <wsdl:portType> ***
2156
+ if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) {
2157
+ $this->wsdl->portTypes[$service_name . 'Port'] = array();
2158
+ }
2159
+ $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port'];
2160
+
2161
+ foreach ($object->__dispatch_map as $operationName => $messages) {
2162
+ $thisPortType[$operationName] = array('name' => $operationName);
2163
+
2164
+ foreach ($messages as $messageType => $messageParts) {
2165
+ switch ($messageType) {
2166
+ case 'in':
2167
+ $thisPortType[$operationName]['input'] = array(
2168
+ 'message' => $operationName . 'Request',
2169
+ 'namespace' => $this->tnsPrefix);
2170
+ break;
2171
+
2172
+ case 'out':
2173
+ $thisPortType[$operationName]['output'] = array(
2174
+ 'message' => $operationName . 'Response',
2175
+ 'namespace' => $this->tnsPrefix);
2176
+ break;
2177
+ }
2178
+ }
2179
+ }
2180
+
2181
+ return true;
2182
+ }
2183
+
2184
+ /**
2185
+ * Takes all the abstract WSDL data and builds concrete bindings and
2186
+ * services (destructive).
2187
+ *
2188
+ * @access private
2189
+ * @todo Current implementation discards $service_desc.
2190
+ *
2191
+ * @param string $schemaNamespace Namespace for types etc.
2192
+ * @param string $service_name Name of the WSDL <service>.
2193
+ * @param string $service_desc Optional description of the WSDL
2194
+ * <service>.
2195
+ */
2196
+ function _generateBindingsAndServices($schemaNamespace, $service_name,
2197
+ $service_desc = '')
2198
+ {
2199
+ // Populate tree with bindings information
2200
+ // XXX Current implementation only supports one binding that
2201
+ // matches the single portType and all of its operations.
2202
+ // XXX Is this the correct use of $schemaNamespace here?
2203
+ // *** <wsdl:binding> ***
2204
+ $this->wsdl->bindings[$service_name . 'Binding'] = array(
2205
+ 'type' => $service_name . 'Port',
2206
+ 'namespace' => $this->tnsPrefix,
2207
+ 'style' => 'rpc',
2208
+ 'transport' => SCHEMA_SOAP_HTTP,
2209
+ 'operations' => array());
2210
+ $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding'];
2211
+
2212
+ foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) {
2213
+ $thisBinding['operations'][$operationName] = array(
2214
+ 'soapAction' => $schemaNamespace . '#' . $operationName,
2215
+ 'style' => $thisBinding['style']);
2216
+
2217
+ foreach (array('input', 'output') as $messageType)
2218
+ if (isset($operationData[$messageType])) {
2219
+ $thisBinding['operations'][$operationName][$messageType] = array(
2220
+ 'use' => 'encoded',
2221
+ 'namespace' => $schemaNamespace,
2222
+ 'encodingStyle' => SOAP_SCHEMA_ENCODING);
2223
+ }
2224
+ }
2225
+
2226
+ // Populate tree with service information
2227
+ // XXX Current implementation supports one service which groups
2228
+ // all of the ports together, one port per binding
2229
+ // *** <wsdl:service> ***
2230
+
2231
+ $this->wsdl->services[$service_name . 'Service'] = array('ports' => array());
2232
+ $thisService =& $this->wsdl->services[$service_name . 'Service']['ports'];
2233
+ $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) ||
2234
+ getenv('SSL_PROTOCOL_VERSION');
2235
+
2236
+ foreach ($this->wsdl->bindings as $bindingName => $bindingData) {
2237
+ $thisService[$bindingData['type']] = array(
2238
+ 'name' => $bindingData['type'],
2239
+ 'binding' => $bindingName,
2240
+ 'namespace' => $this->tnsPrefix,
2241
+ 'address' => array('location' =>
2242
+ ($https ? 'https://' : 'http://') .
2243
+ $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] .
2244
+ (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')),
2245
+ 'type' => 'soap');
2246
+ }
2247
+
2248
+ // Set service
2249
+ $this->wsdl->set_service($service_name . 'Service');
2250
+ $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix];
2251
+
2252
+ // Create WSDL definition
2253
+ // *** <wsdl:definitions> ***
2254
+
2255
+ $this->wsdl->definition = array(
2256
+ 'name' => $service_name,
2257
+ 'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix],
2258
+ 'xmlns' => SCHEMA_WSDL);
2259
+
2260
+ foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) {
2261
+ $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace;
2262
+ }
2263
+ }
2264
+
2265
+ /**
2266
+ * This function is adapted from Dmitri V's implementation of
2267
+ * DISCO/WSDL generation. It separates namespace from type name in
2268
+ * a __typedef key and creates a new namespace entry in the WSDL
2269
+ * structure if the namespace has not been used before. The
2270
+ * namespace prefix and type name are returned. If no namespace is
2271
+ * specified, xsd is assumed.
2272
+ *
2273
+ * We will not need this function anymore once __typedef is
2274
+ * eliminated.
2275
+ */
2276
+ function _getTypeNs($type)
2277
+ {
2278
+ preg_match_all('/\{(.*)\}/sm', $type, $m);
2279
+ if (!empty($m[1][0])) {
2280
+ if (!isset($this->wsdl->ns[$m[1][0]])) {
2281
+ $ns_pref = 'ns' . count($this->wsdl->namespaces);
2282
+ $this->wsdl->ns[$m[1][0]] = $ns_pref;
2283
+ $this->wsdl->namespaces[$ns_pref] = $m[1][0];
2284
+ }
2285
+ $typens = $this->wsdl->ns[$m[1][0]];
2286
+ $type = str_replace($m[0][0], '', $type);
2287
+ } else {
2288
+ $typens = 'xsd';
2289
+ }
2290
+
2291
+ return array($typens, $type);
2292
+ }
2293
+
2294
+ }
lib/PEAR/XML/Parser.php ADDED
@@ -0,0 +1,768 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
+
5
+ /**
6
+ * XML_Parser
7
+ *
8
+ * XML Parser package
9
+ *
10
+ * PHP versions 4 and 5
11
+ *
12
+ * LICENSE:
13
+ *
14
+ * Copyright (c) 2002-2008 The PHP Group
15
+ * All rights reserved.
16
+ *
17
+ * Redistribution and use in source and binary forms, with or without
18
+ * modification, are permitted provided that the following conditions
19
+ * are met:
20
+ *
21
+ * * Redistributions of source code must retain the above copyright
22
+ * notice, this list of conditions and the following disclaimer.
23
+ * * Redistributions in binary form must reproduce the above copyright
24
+ * notice, this list of conditions and the following disclaimer in the
25
+ * documentation and/or other materials provided with the distribution.
26
+ * * The name of the author may not be used to endorse or promote products
27
+ * derived from this software without specific prior written permission.
28
+ *
29
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
37
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+ *
41
+ * @category XML
42
+ * @package XML_Parser
43
+ * @author Stig Bakken <ssb@fast.no>
44
+ * @author Tomas V.V.Cox <cox@idecnet.com>
45
+ * @author Stephan Schmidt <schst@php.net>
46
+ * @copyright 2002-2008 The PHP Group
47
+ * @license http://opensource.org/licenses/bsd-license New BSD License
48
+ * @version CVS: $Id: Parser.php,v 1.30 2008/09/16 16:06:22 ashnazg Exp $
49
+ * @link http://pear.php.net/package/XML_Parser
50
+ */
51
+
52
+ /**
53
+ * uses PEAR's error handling
54
+ */
55
+ require_once 'PEAR.php';
56
+
57
+ /**
58
+ * resource could not be created
59
+ */
60
+ define('XML_PARSER_ERROR_NO_RESOURCE', 200);
61
+
62
+ /**
63
+ * unsupported mode
64
+ */
65
+ define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
66
+
67
+ /**
68
+ * invalid encoding was given
69
+ */
70
+ define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
71
+
72
+ /**
73
+ * specified file could not be read
74
+ */
75
+ define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
76
+
77
+ /**
78
+ * invalid input
79
+ */
80
+ define('XML_PARSER_ERROR_INVALID_INPUT', 204);
81
+
82
+ /**
83
+ * remote file cannot be retrieved in safe mode
84
+ */
85
+ define('XML_PARSER_ERROR_REMOTE', 205);
86
+
87
+ /**
88
+ * XML Parser class.
89
+ *
90
+ * This is an XML parser based on PHP's "xml" extension,
91
+ * based on the bundled expat library.
92
+ *
93
+ * Notes:
94
+ * - It requires PHP 4.0.4pl1 or greater
95
+ * - From revision 1.17, the function names used by the 'func' mode
96
+ * are in the format "xmltag_$elem", for example: use "xmltag_name"
97
+ * to handle the <name></name> tags of your xml file.
98
+ * - different parsing modes
99
+ *
100
+ * @category XML
101
+ * @package XML_Parser
102
+ * @author Stig Bakken <ssb@fast.no>
103
+ * @author Tomas V.V.Cox <cox@idecnet.com>
104
+ * @author Stephan Schmidt <schst@php.net>
105
+ * @copyright 2002-2008 The PHP Group
106
+ * @license http://opensource.org/licenses/bsd-license New BSD License
107
+ * @version Release: @package_version@
108
+ * @link http://pear.php.net/package/XML_Parser
109
+ * @todo create XML_Parser_Namespace to parse documents with namespaces
110
+ * @todo create XML_Parser_Pull
111
+ * @todo Tests that need to be made:
112
+ * - mixing character encodings
113
+ * - a test using all expat handlers
114
+ * - options (folding, output charset)
115
+ */
116
+ class XML_Parser extends PEAR
117
+ {
118
+ // {{{ properties
119
+
120
+ /**
121
+ * XML parser handle
122
+ *
123
+ * @var resource
124
+ * @see xml_parser_create()
125
+ */
126
+ var $parser;
127
+
128
+ /**
129
+ * File handle if parsing from a file
130
+ *
131
+ * @var resource
132
+ */
133
+ var $fp;
134
+
135
+ /**
136
+ * Whether to do case folding
137
+ *
138
+ * If set to true, all tag and attribute names will
139
+ * be converted to UPPER CASE.
140
+ *
141
+ * @var boolean
142
+ */
143
+ var $folding = true;
144
+
145
+ /**
146
+ * Mode of operation, one of "event" or "func"
147
+ *
148
+ * @var string
149
+ */
150
+ var $mode;
151
+
152
+ /**
153
+ * Mapping from expat handler function to class method.
154
+ *
155
+ * @var array
156
+ */
157
+ var $handler = array(
158
+ 'character_data_handler' => 'cdataHandler',
159
+ 'default_handler' => 'defaultHandler',
160
+ 'processing_instruction_handler' => 'piHandler',
161
+ 'unparsed_entity_decl_handler' => 'unparsedHandler',
162
+ 'notation_decl_handler' => 'notationHandler',
163
+ 'external_entity_ref_handler' => 'entityrefHandler'
164
+ );
165
+
166
+ /**
167
+ * source encoding
168
+ *
169
+ * @var string
170
+ */
171
+ var $srcenc;
172
+
173
+ /**
174
+ * target encoding
175
+ *
176
+ * @var string
177
+ */
178
+ var $tgtenc;
179
+
180
+ /**
181
+ * handler object
182
+ *
183
+ * @var object
184
+ */
185
+ var $_handlerObj;
186
+
187
+ /**
188
+ * valid encodings
189
+ *
190
+ * @var array
191
+ */
192
+ var $_validEncodings = array('ISO-8859-1', 'UTF-8', 'US-ASCII');
193
+
194
+ // }}}
195
+ // {{{ php4 constructor
196
+
197
+ /**
198
+ * Creates an XML parser.
199
+ *
200
+ * This is needed for PHP4 compatibility, it will
201
+ * call the constructor, when a new instance is created.
202
+ *
203
+ * @param string $srcenc source charset encoding, use NULL (default) to use
204
+ * whatever the document specifies
205
+ * @param string $mode how this parser object should work, "event" for
206
+ * startelement/endelement-type events, "func"
207
+ * to have it call functions named after elements
208
+ * @param string $tgtenc a valid target encoding
209
+ */
210
+ function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
211
+ {
212
+ XML_Parser::__construct($srcenc, $mode, $tgtenc);
213
+ }
214
+ // }}}
215
+ // {{{ php5 constructor
216
+
217
+ /**
218
+ * PHP5 constructor
219
+ *
220
+ * @param string $srcenc source charset encoding, use NULL (default) to use
221
+ * whatever the document specifies
222
+ * @param string $mode how this parser object should work, "event" for
223
+ * startelement/endelement-type events, "func"
224
+ * to have it call functions named after elements
225
+ * @param string $tgtenc a valid target encoding
226
+ */
227
+ function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
228
+ {
229
+ $this->PEAR('XML_Parser_Error');
230
+
231
+ $this->mode = $mode;
232
+ $this->srcenc = $srcenc;
233
+ $this->tgtenc = $tgtenc;
234
+ }
235
+ // }}}
236
+
237
+ /**
238
+ * Sets the mode of the parser.
239
+ *
240
+ * Possible modes are:
241
+ * - func
242
+ * - event
243
+ *
244
+ * You can set the mode using the second parameter
245
+ * in the constructor.
246
+ *
247
+ * This method is only needed, when switching to a new
248
+ * mode at a later point.
249
+ *
250
+ * @param string $mode mode, either 'func' or 'event'
251
+ *
252
+ * @return boolean|object true on success, PEAR_Error otherwise
253
+ * @access public
254
+ */
255
+ function setMode($mode)
256
+ {
257
+ if ($mode != 'func' && $mode != 'event') {
258
+ $this->raiseError('Unsupported mode given',
259
+ XML_PARSER_ERROR_UNSUPPORTED_MODE);
260
+ }
261
+
262
+ $this->mode = $mode;
263
+ return true;
264
+ }
265
+
266
+ /**
267
+ * Sets the object, that will handle the XML events
268
+ *
269
+ * This allows you to create a handler object independent of the
270
+ * parser object that you are using and easily switch the underlying
271
+ * parser.
272
+ *
273
+ * If no object will be set, XML_Parser assumes that you
274
+ * extend this class and handle the events in $this.
275
+ *
276
+ * @param object &$obj object to handle the events
277
+ *
278
+ * @return boolean will always return true
279
+ * @access public
280
+ * @since v1.2.0beta3
281
+ */
282
+ function setHandlerObj(&$obj)
283
+ {
284
+ $this->_handlerObj = &$obj;
285
+ return true;
286
+ }
287
+
288
+ /**
289
+ * Init the element handlers
290
+ *
291
+ * @return mixed
292
+ * @access private
293
+ */
294
+ function _initHandlers()
295
+ {
296
+ if (!is_resource($this->parser)) {
297
+ return false;
298
+ }
299
+
300
+ if (!is_object($this->_handlerObj)) {
301
+ $this->_handlerObj = &$this;
302
+ }
303
+ switch ($this->mode) {
304
+
305
+ case 'func':
306
+ xml_set_object($this->parser, $this->_handlerObj);
307
+ xml_set_element_handler($this->parser,
308
+ array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
309
+ break;
310
+
311
+ case 'event':
312
+ xml_set_object($this->parser, $this->_handlerObj);
313
+ xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
314
+ break;
315
+ default:
316
+ return $this->raiseError('Unsupported mode given',
317
+ XML_PARSER_ERROR_UNSUPPORTED_MODE);
318
+ break;
319
+ }
320
+
321
+ /**
322
+ * set additional handlers for character data, entities, etc.
323
+ */
324
+ foreach ($this->handler as $xml_func => $method) {
325
+ if (method_exists($this->_handlerObj, $method)) {
326
+ $xml_func = 'xml_set_' . $xml_func;
327
+ $xml_func($this->parser, $method);
328
+ }
329
+ }
330
+ }
331
+
332
+ // {{{ _create()
333
+
334
+ /**
335
+ * create the XML parser resource
336
+ *
337
+ * Has been moved from the constructor to avoid
338
+ * problems with object references.
339
+ *
340
+ * Furthermore it allows us returning an error
341
+ * if something fails.
342
+ *
343
+ * NOTE: uses '@' error suppresion in this method
344
+ *
345
+ * @return bool|PEAR_Error true on success, PEAR_Error otherwise
346
+ * @access private
347
+ * @see xml_parser_create
348
+ */
349
+ function _create()
350
+ {
351
+ if ($this->srcenc === null) {
352
+ $xp = @xml_parser_create();
353
+ } else {
354
+ $xp = @xml_parser_create($this->srcenc);
355
+ }
356
+ if (is_resource($xp)) {
357
+ if ($this->tgtenc !== null) {
358
+ if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
359
+ $this->tgtenc)
360
+ ) {
361
+ return $this->raiseError('invalid target encoding',
362
+ XML_PARSER_ERROR_INVALID_ENCODING);
363
+ }
364
+ }
365
+ $this->parser = $xp;
366
+ $result = $this->_initHandlers($this->mode);
367
+ if ($this->isError($result)) {
368
+ return $result;
369
+ }
370
+ xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
371
+ return true;
372
+ }
373
+ if (!in_array(strtoupper($this->srcenc), $this->_validEncodings)) {
374
+ return $this->raiseError('invalid source encoding',
375
+ XML_PARSER_ERROR_INVALID_ENCODING);
376
+ }
377
+ return $this->raiseError('Unable to create XML parser resource.',
378
+ XML_PARSER_ERROR_NO_RESOURCE);
379
+ }
380
+
381
+ // }}}
382
+ // {{{ reset()
383
+
384
+ /**
385
+ * Reset the parser.
386
+ *
387
+ * This allows you to use one parser instance
388
+ * to parse multiple XML documents.
389
+ *
390
+ * @access public
391
+ * @return boolean|object true on success, PEAR_Error otherwise
392
+ */
393
+ function reset()
394
+ {
395
+ $result = $this->_create();
396
+ if ($this->isError($result)) {
397
+ return $result;
398
+ }
399
+ return true;
400
+ }
401
+
402
+ // }}}
403
+ // {{{ setInputFile()
404
+
405
+ /**
406
+ * Sets the input xml file to be parsed
407
+ *
408
+ * @param string $file Filename (full path)
409
+ *
410
+ * @return resource fopen handle of the given file
411
+ * @access public
412
+ * @throws XML_Parser_Error
413
+ * @see setInput(), setInputString(), parse()
414
+ */
415
+ function setInputFile($file)
416
+ {
417
+ /**
418
+ * check, if file is a remote file
419
+ */
420
+ if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
421
+ if (!ini_get('allow_url_fopen')) {
422
+ return $this->
423
+ raiseError('Remote files cannot be parsed, as safe mode is enabled.',
424
+ XML_PARSER_ERROR_REMOTE);
425
+ }
426
+ }
427
+
428
+ $fp = @fopen($file, 'rb');
429
+ if (is_resource($fp)) {
430
+ $this->fp = $fp;
431
+ return $fp;
432
+ }
433
+ return $this->raiseError('File could not be opened.',
434
+ XML_PARSER_ERROR_FILE_NOT_READABLE);
435
+ }
436
+
437
+ // }}}
438
+ // {{{ setInputString()
439
+
440
+ /**
441
+ * XML_Parser::setInputString()
442
+ *
443
+ * Sets the xml input from a string
444
+ *
445
+ * @param string $data a string containing the XML document
446
+ *
447
+ * @return null
448
+ */
449
+ function setInputString($data)
450
+ {
451
+ $this->fp = $data;
452
+ return null;
453
+ }
454
+
455
+ // }}}
456
+ // {{{ setInput()
457
+
458
+ /**
459
+ * Sets the file handle to use with parse().
460
+ *
461
+ * You should use setInputFile() or setInputString() if you
462
+ * pass a string
463
+ *
464
+ * @param mixed $fp Can be either a resource returned from fopen(),
465
+ * a URL, a local filename or a string.
466
+ *
467
+ * @return mixed
468
+ * @access public
469
+ * @see parse()
470
+ * @uses setInputString(), setInputFile()
471
+ */
472
+ function setInput($fp)
473
+ {
474
+ if (is_resource($fp)) {
475
+ $this->fp = $fp;
476
+ return true;
477
+ } elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
478
+ // see if it's an absolute URL (has a scheme at the beginning)
479
+ return $this->setInputFile($fp);
480
+ } elseif (file_exists($fp)) {
481
+ // see if it's a local file
482
+ return $this->setInputFile($fp);
483
+ } else {
484
+ // it must be a string
485
+ $this->fp = $fp;
486
+ return true;
487
+ }
488
+
489
+ return $this->raiseError('Illegal input format',
490
+ XML_PARSER_ERROR_INVALID_INPUT);
491
+ }
492
+
493
+ // }}}
494
+ // {{{ parse()
495
+
496
+ /**
497
+ * Central parsing function.
498
+ *
499
+ * @return bool|PEAR_Error returns true on success, or a PEAR_Error otherwise
500
+ * @access public
501
+ */
502
+ function parse()
503
+ {
504
+ /**
505
+ * reset the parser
506
+ */
507
+ $result = $this->reset();
508
+ if ($this->isError($result)) {
509
+ return $result;
510
+ }
511
+ // if $this->fp was fopened previously
512
+ if (is_resource($this->fp)) {
513
+
514
+ while ($data = fread($this->fp, 4096)) {
515
+ if (!$this->_parseString($data, feof($this->fp))) {
516
+ $error = &$this->raiseError();
517
+ $this->free();
518
+ return $error;
519
+ }
520
+ }
521
+ } else {
522
+ // otherwise, $this->fp must be a string
523
+ if (!$this->_parseString($this->fp, true)) {
524
+ $error = &$this->raiseError();
525
+ $this->free();
526
+ return $error;
527
+ }
528
+ }
529
+ $this->free();
530
+
531
+ return true;
532
+ }
533
+
534
+ /**
535
+ * XML_Parser::_parseString()
536
+ *
537
+ * @param string $data data
538
+ * @param bool $eof end-of-file flag
539
+ *
540
+ * @return bool
541
+ * @access private
542
+ * @see parseString()
543
+ **/
544
+ function _parseString($data, $eof = false)
545
+ {
546
+ return xml_parse($this->parser, $data, $eof);
547
+ }
548
+
549
+ // }}}
550
+ // {{{ parseString()
551
+
552
+ /**
553
+ * XML_Parser::parseString()
554
+ *
555
+ * Parses a string.
556
+ *
557
+ * @param string $data XML data
558
+ * @param boolean $eof If set and TRUE, data is the last piece
559
+ * of data sent in this parser
560
+ *
561
+ * @return bool|PEAR_Error true on success or a PEAR Error
562
+ * @throws XML_Parser_Error
563
+ * @see _parseString()
564
+ */
565
+ function parseString($data, $eof = false)
566
+ {
567
+ if (!isset($this->parser) || !is_resource($this->parser)) {
568
+ $this->reset();
569
+ }
570
+
571
+ if (!$this->_parseString($data, $eof)) {
572
+ $error = &$this->raiseError();
573
+ $this->free();
574
+ return $error;
575
+ }
576
+
577
+ if ($eof === true) {
578
+ $this->free();
579
+ }
580
+ return true;
581
+ }
582
+
583
+ /**
584
+ * XML_Parser::free()
585
+ *
586
+ * Free the internal resources associated with the parser
587
+ *
588
+ * @return null
589
+ **/
590
+ function free()
591
+ {
592
+ if (isset($this->parser) && is_resource($this->parser)) {
593
+ xml_parser_free($this->parser);
594
+ unset( $this->parser );
595
+ }
596
+ if (isset($this->fp) && is_resource($this->fp)) {
597
+ fclose($this->fp);
598
+ }
599
+ unset($this->fp);
600
+ return null;
601
+ }
602
+
603
+ /**
604
+ * XML_Parser::raiseError()
605
+ *
606
+ * Throws a XML_Parser_Error
607
+ *
608
+ * @param string $msg the error message
609
+ * @param integer $ecode the error message code
610
+ *
611
+ * @return XML_Parser_Error reference to the error object
612
+ **/
613
+ function &raiseError($msg = null, $ecode = 0)
614
+ {
615
+ $msg = !is_null($msg) ? $msg : $this->parser;
616
+ $err = &new XML_Parser_Error($msg, $ecode);
617
+ return parent::raiseError($err);
618
+ }
619
+
620
+ // }}}
621
+ // {{{ funcStartHandler()
622
+
623
+ /**
624
+ * derives and calls the Start Handler function
625
+ *
626
+ * @param mixed $xp ??
627
+ * @param mixed $elem ??
628
+ * @param mixed $attribs ??
629
+ *
630
+ * @return void
631
+ */
632
+ function funcStartHandler($xp, $elem, $attribs)
633
+ {
634
+ $func = 'xmltag_' . $elem;
635
+ $func = str_replace(array('.', '-', ':'), '_', $func);
636
+ if (method_exists($this->_handlerObj, $func)) {
637
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
638
+ } elseif (method_exists($this->_handlerObj, 'xmltag')) {
639
+ call_user_func(array(&$this->_handlerObj, 'xmltag'),
640
+ $xp, $elem, $attribs);
641
+ }
642
+ }
643
+
644
+ // }}}
645
+ // {{{ funcEndHandler()
646
+
647
+ /**
648
+ * derives and calls the End Handler function
649
+ *
650
+ * @param mixed $xp ??
651
+ * @param mixed $elem ??
652
+ *
653
+ * @return void
654
+ */
655
+ function funcEndHandler($xp, $elem)
656
+ {
657
+ $func = 'xmltag_' . $elem . '_';
658
+ $func = str_replace(array('.', '-', ':'), '_', $func);
659
+ if (method_exists($this->_handlerObj, $func)) {
660
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
661
+ } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
662
+ call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
663
+ }
664
+ }
665
+
666
+ // }}}
667
+ // {{{ startHandler()
668
+
669
+ /**
670
+ * abstract method signature for Start Handler
671
+ *
672
+ * @param mixed $xp ??
673
+ * @param mixed $elem ??
674
+ * @param mixed &$attribs ??
675
+ *
676
+ * @return null
677
+ * @abstract
678
+ */
679
+ function startHandler($xp, $elem, &$attribs)
680
+ {
681
+ return null;
682
+ }
683
+
684
+ // }}}
685
+ // {{{ endHandler()
686
+
687
+ /**
688
+ * abstract method signature for End Handler
689
+ *
690
+ * @param mixed $xp ??
691
+ * @param mixed $elem ??
692
+ *
693
+ * @return null
694
+ * @abstract
695
+ */
696
+ function endHandler($xp, $elem)
697
+ {
698
+ return null;
699
+ }
700
+
701
+
702
+ // }}}me
703
+ }
704
+
705
+ /**
706
+ * error class, replaces PEAR_Error
707
+ *
708
+ * An instance of this class will be returned
709
+ * if an error occurs inside XML_Parser.
710
+ *
711
+ * There are three advantages over using the standard PEAR_Error:
712
+ * - All messages will be prefixed
713
+ * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
714
+ * - messages can be generated from the xml_parser resource
715
+ *
716
+ * @category XML
717
+ * @package XML_Parser
718
+ * @author Stig Bakken <ssb@fast.no>
719
+ * @author Tomas V.V.Cox <cox@idecnet.com>
720
+ * @author Stephan Schmidt <schst@php.net>
721
+ * @copyright 2002-2008 The PHP Group
722
+ * @license http://opensource.org/licenses/bsd-license New BSD License
723
+ * @version Release: @package_version@
724
+ * @link http://pear.php.net/package/XML_Parser
725
+ * @see PEAR_Error
726
+ */
727
+ class XML_Parser_Error extends PEAR_Error
728
+ {
729
+ // {{{ properties
730
+
731
+ /**
732
+ * prefix for all messages
733
+ *
734
+ * @var string
735
+ */
736
+ var $error_message_prefix = 'XML_Parser: ';
737
+
738
+ // }}}
739
+ // {{{ constructor()
740
+ /**
741
+ * construct a new error instance
742
+ *
743
+ * You may either pass a message or an xml_parser resource as first
744
+ * parameter. If a resource has been passed, the last error that
745
+ * happened will be retrieved and returned.
746
+ *
747
+ * @param string|resource $msgorparser message or parser resource
748
+ * @param integer $code error code
749
+ * @param integer $mode error handling
750
+ * @param integer $level error level
751
+ *
752
+ * @access public
753
+ * @todo PEAR CS - can't meet 85char line limit without arg refactoring
754
+ */
755
+ function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
756
+ {
757
+ if (is_resource($msgorparser)) {
758
+ $code = xml_get_error_code($msgorparser);
759
+ $msgorparser = sprintf('%s at XML input line %d:%d',
760
+ xml_error_string($code),
761
+ xml_get_current_line_number($msgorparser),
762
+ xml_get_current_column_number($msgorparser));
763
+ }
764
+ $this->PEAR_Error($msgorparser, $code, $mode, $level);
765
+ }
766
+ // }}}
767
+ }
768
+ ?>
lib/PEAR/XML/Parser/Simple.php ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
+
5
+ /**
6
+ * XML_Parser
7
+ *
8
+ * XML Parser's Simple parser class
9
+ *
10
+ * PHP versions 4 and 5
11
+ *
12
+ * LICENSE:
13
+ *
14
+ * Copyright (c) 2002-2008 The PHP Group
15
+ * All rights reserved.
16
+ *
17
+ * Redistribution and use in source and binary forms, with or without
18
+ * modification, are permitted provided that the following conditions
19
+ * are met:
20
+ *
21
+ * * Redistributions of source code must retain the above copyright
22
+ * notice, this list of conditions and the following disclaimer.
23
+ * * Redistributions in binary form must reproduce the above copyright
24
+ * notice, this list of conditions and the following disclaimer in the
25
+ * documentation and/or other materials provided with the distribution.
26
+ * * The name of the author may not be used to endorse or promote products
27
+ * derived from this software without specific prior written permission.
28
+ *
29
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
37
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+ *
41
+ * @category XML
42
+ * @package XML_Parser
43
+ * @author Stephan Schmidt <schst@php.net>
44
+ * @copyright 2004-2008 Stephan Schmidt <schst@php.net>
45
+ * @license http://opensource.org/licenses/bsd-license New BSD License
46
+ * @version CVS: $Id: Simple.php,v 1.7 2008/08/24 21:48:21 ashnazg Exp $
47
+ * @link http://pear.php.net/package/XML_Parser
48
+ */
49
+
50
+ /**
51
+ * built on XML_Parser
52
+ */
53
+ require_once 'XML/Parser.php';
54
+
55
+ /**
56
+ * Simple XML parser class.
57
+ *
58
+ * This class is a simplified version of XML_Parser.
59
+ * In most XML applications the real action is executed,
60
+ * when a closing tag is found.
61
+ *
62
+ * XML_Parser_Simple allows you to just implement one callback
63
+ * for each tag that will receive the tag with its attributes
64
+ * and CData.
65
+ *
66
+ * <code>
67
+ * require_once '../Parser/Simple.php';
68
+ *
69
+ * class myParser extends XML_Parser_Simple
70
+ * {
71
+ * function myParser()
72
+ * {
73
+ * $this->XML_Parser_Simple();
74
+ * }
75
+ *
76
+ * function handleElement($name, $attribs, $data)
77
+ * {
78
+ * printf('handle %s<br>', $name);
79
+ * }
80
+ * }
81
+ *
82
+ * $p = &new myParser();
83
+ *
84
+ * $result = $p->setInputFile('myDoc.xml');
85
+ * $result = $p->parse();
86
+ * </code>
87
+ *
88
+ * @category XML
89
+ * @package XML_Parser
90
+ * @author Stephan Schmidt <schst@php.net>
91
+ * @copyright 2004-2008 The PHP Group
92
+ * @license http://opensource.org/licenses/bsd-license New BSD License
93
+ * @version Release: @package_version@
94
+ * @link http://pear.php.net/package/XML_Parser
95
+ */
96
+ class XML_Parser_Simple extends XML_Parser
97
+ {
98
+ /**
99
+ * element stack
100
+ *
101
+ * @access private
102
+ * @var array
103
+ */
104
+ var $_elStack = array();
105
+
106
+ /**
107
+ * all character data
108
+ *
109
+ * @access private
110
+ * @var array
111
+ */
112
+ var $_data = array();
113
+
114
+ /**
115
+ * element depth
116
+ *
117
+ * @access private
118
+ * @var integer
119
+ */
120
+ var $_depth = 0;
121
+
122
+ /**
123
+ * Mapping from expat handler function to class method.
124
+ *
125
+ * @var array
126
+ */
127
+ var $handler = array(
128
+ 'default_handler' => 'defaultHandler',
129
+ 'processing_instruction_handler' => 'piHandler',
130
+ 'unparsed_entity_decl_handler' => 'unparsedHandler',
131
+ 'notation_decl_handler' => 'notationHandler',
132
+ 'external_entity_ref_handler' => 'entityrefHandler'
133
+ );
134
+
135
+ /**
136
+ * Creates an XML parser.
137
+ *
138
+ * This is needed for PHP4 compatibility, it will
139
+ * call the constructor, when a new instance is created.
140
+ *
141
+ * @param string $srcenc source charset encoding, use NULL (default) to use
142
+ * whatever the document specifies
143
+ * @param string $mode how this parser object should work, "event" for
144
+ * handleElement(), "func" to have it call functions
145
+ * named after elements (handleElement_$name())
146
+ * @param string $tgtenc a valid target encoding
147
+ */
148
+ function XML_Parser_Simple($srcenc = null, $mode = 'event', $tgtenc = null)
149
+ {
150
+ $this->XML_Parser($srcenc, $mode, $tgtenc);
151
+ }
152
+
153
+ /**
154
+ * inits the handlers
155
+ *
156
+ * @return mixed
157
+ * @access private
158
+ */
159
+ function _initHandlers()
160
+ {
161
+ if (!is_object($this->_handlerObj)) {
162
+ $this->_handlerObj = &$this;
163
+ }
164
+
165
+ if ($this->mode != 'func' && $this->mode != 'event') {
166
+ return $this->raiseError('Unsupported mode given',
167
+ XML_PARSER_ERROR_UNSUPPORTED_MODE);
168
+ }
169
+ xml_set_object($this->parser, $this->_handlerObj);
170
+
171
+ xml_set_element_handler($this->parser, array(&$this, 'startHandler'),
172
+ array(&$this, 'endHandler'));
173
+ xml_set_character_data_handler($this->parser, array(&$this, 'cdataHandler'));
174
+
175
+ /**
176
+ * set additional handlers for character data, entities, etc.
177
+ */
178
+ foreach ($this->handler as $xml_func => $method) {
179
+ if (method_exists($this->_handlerObj, $method)) {
180
+ $xml_func = 'xml_set_' . $xml_func;
181
+ $xml_func($this->parser, $method);
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Reset the parser.
188
+ *
189
+ * This allows you to use one parser instance
190
+ * to parse multiple XML documents.
191
+ *
192
+ * @access public
193
+ * @return boolean|object true on success, PEAR_Error otherwise
194
+ */
195
+ function reset()
196
+ {
197
+ $this->_elStack = array();
198
+ $this->_data = array();
199
+ $this->_depth = 0;
200
+
201
+ $result = $this->_create();
202
+ if ($this->isError($result)) {
203
+ return $result;
204
+ }
205
+ return true;
206
+ }
207
+
208
+ /**
209
+ * start handler
210
+ *
211
+ * Pushes attributes and tagname onto a stack
212
+ *
213
+ * @param resource $xp xml parser resource
214
+ * @param string $elem element name
215
+ * @param array &$attribs attributes
216
+ *
217
+ * @return mixed
218
+ * @access private
219
+ * @final
220
+ */
221
+ function startHandler($xp, $elem, &$attribs)
222
+ {
223
+ array_push($this->_elStack, array(
224
+ 'name' => $elem,
225
+ 'attribs' => $attribs
226
+ ));
227
+ $this->_depth++;
228
+ $this->_data[$this->_depth] = '';
229
+ }
230
+
231
+ /**
232
+ * end handler
233
+ *
234
+ * Pulls attributes and tagname from a stack
235
+ *
236
+ * @param resource $xp xml parser resource
237
+ * @param string $elem element name
238
+ *
239
+ * @return mixed
240
+ * @access private
241
+ * @final
242
+ */
243
+ function endHandler($xp, $elem)
244
+ {
245
+ $el = array_pop($this->_elStack);
246
+ $data = $this->_data[$this->_depth];
247
+ $this->_depth--;
248
+
249
+ switch ($this->mode) {
250
+ case 'event':
251
+ $this->_handlerObj->handleElement($el['name'], $el['attribs'], $data);
252
+ break;
253
+ case 'func':
254
+ $func = 'handleElement_' . $elem;
255
+ if (strchr($func, '.')) {
256
+ $func = str_replace('.', '_', $func);
257
+ }
258
+ if (method_exists($this->_handlerObj, $func)) {
259
+ call_user_func(array(&$this->_handlerObj, $func),
260
+ $el['name'], $el['attribs'], $data);
261
+ }
262
+ break;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * handle character data
268
+ *
269
+ * @param resource $xp xml parser resource
270
+ * @param string $data data
271
+ *
272
+ * @return void
273
+ * @access private
274
+ * @final
275
+ */
276
+ function cdataHandler($xp, $data)
277
+ {
278
+ $this->_data[$this->_depth] .= $data;
279
+ }
280
+
281
+ /**
282
+ * handle a tag
283
+ *
284
+ * Implement this in your parser
285
+ *
286
+ * @param string $name element name
287
+ * @param array $attribs attributes
288
+ * @param string $data character data
289
+ *
290
+ * @return void
291
+ * @access public
292
+ * @abstract
293
+ */
294
+ function handleElement($name, $attribs, $data)
295
+ {
296
+ }
297
+
298
+ /**
299
+ * get the current tag depth
300
+ *
301
+ * The root tag is in depth 0.
302
+ *
303
+ * @access public
304
+ * @return integer
305
+ */
306
+ function getCurrentDepth()
307
+ {
308
+ return $this->_depth;
309
+ }
310
+
311
+ /**
312
+ * add some string to the current ddata.
313
+ *
314
+ * This is commonly needed, when a document is parsed recursively.
315
+ *
316
+ * @param string $data data to add
317
+ *
318
+ * @return void
319
+ * @access public
320
+ */
321
+ function addToData($data)
322
+ {
323
+ $this->_data[$this->_depth] .= $data;
324
+ }
325
+ }
326
+ ?>
lib/PEAR/XML/Serializer.php ADDED
@@ -0,0 +1,1222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?PHP
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * XML_Serializer
6
+ *
7
+ * Creates XML documents from PHP data structures like arrays, objects or scalars.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * LICENSE:
12
+ *
13
+ * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
14
+ * All rights reserved.
15
+ *
16
+ * Redistribution and use in source and binary forms, with or without
17
+ * modification, are permitted provided that the following conditions
18
+ * are met:
19
+ *
20
+ * * Redistributions of source code must retain the above copyright
21
+ * notice, this list of conditions and the following disclaimer.
22
+ * * Redistributions in binary form must reproduce the above copyright
23
+ * notice, this list of conditions and the following disclaimer in the
24
+ * documentation and/or other materials provided with the distribution.
25
+ * * The name of the author may not be used to endorse or promote products
26
+ * derived from this software without specific prior written permission.
27
+ *
28
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
36
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ *
40
+ * @category XML
41
+ * @package XML_Serializer
42
+ * @author Stephan Schmidt <schst@php.net>
43
+ * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
44
+ * @license http://opensource.org/licenses/bsd-license New BSD License
45
+ * @version CVS: $Id: Serializer.php,v 1.57 2009/01/25 03:51:11 ashnazg Exp $
46
+ * @link http://pear.php.net/package/XML_Serializer
47
+ * @see XML_Unserializer
48
+ */
49
+
50
+ /**
51
+ * uses PEAR error management
52
+ */
53
+ require_once 'PEAR.php';
54
+
55
+ /**
56
+ * uses XML_Util to create XML tags
57
+ */
58
+ require_once 'XML/Util.php';
59
+
60
+ /**
61
+ * option: string used for indentation
62
+ *
63
+ * Possible values:
64
+ * - any string (default is any string)
65
+ */
66
+ define('XML_SERIALIZER_OPTION_INDENT', 'indent');
67
+
68
+ /**
69
+ * option: string used for linebreaks
70
+ *
71
+ * Possible values:
72
+ * - any string (default is \n)
73
+ */
74
+ define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
75
+
76
+ /**
77
+ * option: enable type hints
78
+ *
79
+ * Possible values:
80
+ * - true
81
+ * - false
82
+ */
83
+ define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
84
+
85
+ /**
86
+ * option: add an XML declaration
87
+ *
88
+ * Possible values:
89
+ * - true
90
+ * - false
91
+ */
92
+ define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl');
93
+
94
+ /**
95
+ * option: encoding of the document
96
+ *
97
+ * Possible values:
98
+ * - any valid encoding
99
+ * - null (default)
100
+ */
101
+ define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding');
102
+
103
+ /**
104
+ * option: default name for tags
105
+ *
106
+ * Possible values:
107
+ * - any string (XML_Serializer_Tag is default)
108
+ */
109
+ define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName');
110
+
111
+ /**
112
+ * option: use classname for objects in indexed arrays
113
+ *
114
+ * Possible values:
115
+ * - true
116
+ * - false (default)
117
+ */
118
+ define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName');
119
+
120
+ /**
121
+ * option: attribute where original key is stored
122
+ *
123
+ * Possible values:
124
+ * - any string (default is _originalKey)
125
+ */
126
+ define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
127
+
128
+ /**
129
+ * option: attribute for type (only if typeHints => true)
130
+ *
131
+ * Possible values:
132
+ * - any string (default is _type)
133
+ */
134
+ define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
135
+
136
+ /**
137
+ * option: attribute for class (only if typeHints => true)
138
+ *
139
+ * Possible values:
140
+ * - any string (default is _class)
141
+ */
142
+ define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
143
+
144
+ /**
145
+ * option: scalar values (strings, ints,..) will be serialized as attribute
146
+ *
147
+ * Possible values:
148
+ * - true
149
+ * - false (default)
150
+ * - array which sets this option on a per-tag basis
151
+ */
152
+ define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes');
153
+
154
+ /**
155
+ * option: prepend string for attributes
156
+ *
157
+ * Possible values:
158
+ * - any string (default is any string)
159
+ */
160
+ define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes');
161
+
162
+ /**
163
+ * option: indent the attributes, if set to '_auto',
164
+ * it will indent attributes so they all start at the same column
165
+ *
166
+ * Possible values:
167
+ * - true
168
+ * - false (default)
169
+ * - '_auto'
170
+ */
171
+ define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes');
172
+
173
+ /**
174
+ * option: use 'simplexml' to use parent name as tagname
175
+ * if transforming an indexed array
176
+ *
177
+ * Possible values:
178
+ * - XML_SERIALIZER_MODE_DEFAULT (default)
179
+ * - XML_SERIALIZER_MODE_SIMPLEXML
180
+ */
181
+ define('XML_SERIALIZER_OPTION_MODE', 'mode');
182
+
183
+ /**
184
+ * option: add a doctype declaration
185
+ *
186
+ * Possible values:
187
+ * - true
188
+ * - false (default)
189
+ */
190
+ define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype');
191
+
192
+ /**
193
+ * option: supply a string or an array with id and uri
194
+ * ({@see XML_Util::getDoctypeDeclaration()}
195
+ *
196
+ * Possible values:
197
+ * - string
198
+ * - array
199
+ */
200
+ define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
201
+
202
+ /**
203
+ * option: name of the root tag
204
+ *
205
+ * Possible values:
206
+ * - string
207
+ * - null (default)
208
+ */
209
+ define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
210
+
211
+ /**
212
+ * option: attributes of the root tag
213
+ *
214
+ * Possible values:
215
+ * - array
216
+ */
217
+ define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes');
218
+
219
+ /**
220
+ * option: all values in this key will be treated as attributes
221
+ *
222
+ * Possible values:
223
+ * - string
224
+ */
225
+ define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray');
226
+
227
+ /**
228
+ * option: this value will be used directly as content,
229
+ * instead of creating a new tag, may only be used
230
+ * in conjuction with attributesArray
231
+ *
232
+ * Possible values:
233
+ * - string
234
+ * - null (default)
235
+ */
236
+ define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName');
237
+
238
+ /**
239
+ * option: this value will be used in a comment, instead of creating a new tag
240
+ *
241
+ * Possible values:
242
+ * - string
243
+ * - null (default)
244
+ */
245
+ define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
246
+
247
+ /**
248
+ * option: tag names that will be changed
249
+ *
250
+ * Possible values:
251
+ * - array
252
+ */
253
+ define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap');
254
+
255
+ /**
256
+ * option: function that will be applied before serializing
257
+ *
258
+ * Possible values:
259
+ * - any valid PHP callback
260
+ */
261
+ define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction');
262
+
263
+ /**
264
+ * option: namespace to use for the document
265
+ *
266
+ * Possible values:
267
+ * - string
268
+ * - null (default)
269
+ */
270
+ define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace');
271
+
272
+ /**
273
+ * option: type of entities to replace
274
+ *
275
+ * Possible values:
276
+ * - XML_SERIALIZER_ENTITIES_NONE
277
+ * - XML_SERIALIZER_ENTITIES_XML (default)
278
+ * - XML_SERIALIZER_ENTITIES_XML_REQUIRED
279
+ * - XML_SERIALIZER_ENTITIES_HTML
280
+ */
281
+ define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities');
282
+
283
+ /**
284
+ * option: whether to return the result of the serialization from serialize()
285
+ *
286
+ * Possible values:
287
+ * - true
288
+ * - false (default)
289
+ */
290
+ define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
291
+
292
+ /**
293
+ * option: whether to ignore properties that are set to null
294
+ *
295
+ * Possible values:
296
+ * - true
297
+ * - false (default)
298
+ */
299
+ define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull');
300
+
301
+ /**
302
+ * option: whether to use cdata sections for character data
303
+ *
304
+ * Possible values:
305
+ * - true
306
+ * - false (default)
307
+ */
308
+ define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata');
309
+
310
+
311
+ /**
312
+ * default mode
313
+ */
314
+ define('XML_SERIALIZER_MODE_DEFAULT', 'default');
315
+
316
+ /**
317
+ * SimpleXML mode
318
+ *
319
+ * When serializing indexed arrays, the key of the parent value is used as a tagname.
320
+ */
321
+ define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
322
+
323
+ /**
324
+ * error code for no serialization done
325
+ */
326
+ define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
327
+
328
+ /**
329
+ * do not replace entitites
330
+ */
331
+ define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
332
+
333
+ /**
334
+ * replace all XML entitites
335
+ * This setting will replace <, >, ", ' and &
336
+ */
337
+ define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
338
+
339
+ /**
340
+ * replace only required XML entitites
341
+ * This setting will replace <, " and &
342
+ */
343
+ define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
344
+
345
+ /**
346
+ * replace HTML entitites
347
+ * @link http://www.php.net/htmlentities
348
+ */
349
+ define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
350
+
351
+ /**
352
+ * Creates XML documents from PHP data structures like arrays, objects or scalars.
353
+ *
354
+ * this class can be used in two modes:
355
+ *
356
+ * 1. create an XML document from an array or object that is processed by other
357
+ * applications. That means, you can create a RDF document from an array in the
358
+ * following format:
359
+ *
360
+ * $data = array(
361
+ * 'channel' => array(
362
+ * 'title' => 'Example RDF channel',
363
+ * 'link' => 'http://www.php-tools.de',
364
+ * 'image' => array(
365
+ * 'title' => 'Example image',
366
+ * 'url' => 'http://www.php-tools.de/image.gif',
367
+ * 'link' => 'http://www.php-tools.de'
368
+ * ),
369
+ * array(
370
+ * 'title' => 'Example item',
371
+ * 'link' => 'http://example.com'
372
+ * ),
373
+ * array(
374
+ * 'title' => 'Another Example item',
375
+ * 'link' => 'http://example.org'
376
+ * )
377
+ * )
378
+ * );
379
+ *
380
+ * to create a RDF document from this array do the following:
381
+ *
382
+ * require_once 'XML/Serializer.php';
383
+ *
384
+ * $options = array(
385
+ * XML_SERIALIZER_OPTION_INDENT => "\t", // indent with tabs
386
+ * XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // use UNIX line breaks
387
+ * XML_SERIALIZER_OPTION_ROOT_NAME => 'rdf:RDF',// root tag
388
+ * XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item' // tag for values
389
+ * // with numeric keys
390
+ * );
391
+ *
392
+ * $serializer = new XML_Serializer($options);
393
+ * $rdf = $serializer->serialize($data);
394
+ *
395
+ * You will get a complete XML document that can be processed like any RDF document.
396
+ *
397
+ * 2. this classes can be used to serialize any data structure in a way that it can
398
+ * later be unserialized again.
399
+ * XML_Serializer will store the type of the value and additional meta information
400
+ * in attributes of the surrounding tag. This meat information can later be used
401
+ * to restore the original data structure in PHP. If you want XML_Serializer
402
+ * to add meta information to the tags, add
403
+ *
404
+ * XML_SERIALIZER_OPTION_TYPEHINTS => true
405
+ *
406
+ * to the options array in the constructor.
407
+ *
408
+ * @category XML
409
+ * @package XML_Serializer
410
+ * @author Stephan Schmidt <schst@php.net>
411
+ * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
412
+ * @license http://opensource.org/licenses/bsd-license New BSD License
413
+ * @version Release: 0.19.2
414
+ * @link http://pear.php.net/package/XML_Serializer
415
+ * @see XML_Unserializer
416
+ */
417
+ class XML_Serializer extends PEAR
418
+ {
419
+ /**
420
+ * list of all available options
421
+ *
422
+ * @access private
423
+ * @var array
424
+ */
425
+ var $_knownOptions = array(
426
+ XML_SERIALIZER_OPTION_INDENT,
427
+ XML_SERIALIZER_OPTION_LINEBREAKS,
428
+ XML_SERIALIZER_OPTION_TYPEHINTS,
429
+ XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
430
+ XML_SERIALIZER_OPTION_XML_ENCODING,
431
+ XML_SERIALIZER_OPTION_DEFAULT_TAG,
432
+ XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
433
+ XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
434
+ XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
435
+ XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
436
+ XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
437
+ XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
438
+ XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
439
+ XML_SERIALIZER_OPTION_MODE,
440
+ XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
441
+ XML_SERIALIZER_OPTION_DOCTYPE,
442
+ XML_SERIALIZER_OPTION_ROOT_NAME,
443
+ XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
444
+ XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
445
+ XML_SERIALIZER_OPTION_CONTENT_KEY,
446
+ XML_SERIALIZER_OPTION_COMMENT_KEY,
447
+ XML_SERIALIZER_OPTION_TAGMAP,
448
+ XML_SERIALIZER_OPTION_ENCODE_FUNC,
449
+ XML_SERIALIZER_OPTION_NAMESPACE,
450
+ XML_SERIALIZER_OPTION_ENTITIES,
451
+ XML_SERIALIZER_OPTION_RETURN_RESULT,
452
+ XML_SERIALIZER_OPTION_IGNORE_NULL,
453
+ XML_SERIALIZER_OPTION_CDATA_SECTIONS
454
+ );
455
+
456
+ /**
457
+ * default options for the serialization
458
+ *
459
+ * @access private
460
+ * @var array
461
+ */
462
+ var $_defaultOptions = array(
463
+
464
+ // string used for indentation
465
+ XML_SERIALIZER_OPTION_INDENT => '',
466
+
467
+ // string used for newlines
468
+ XML_SERIALIZER_OPTION_LINEBREAKS => "\n",
469
+
470
+ // automatically add type hin attributes
471
+ XML_SERIALIZER_OPTION_TYPEHINTS => false,
472
+
473
+ // add an XML declaration
474
+ XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false,
475
+
476
+ // encoding specified in the XML declaration
477
+ XML_SERIALIZER_OPTION_XML_ENCODING => null,
478
+
479
+ // tag used for indexed arrays or invalid names
480
+ XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag',
481
+
482
+ // use classname for objects in indexed arrays
483
+ XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false,
484
+
485
+ // attribute where original key is stored
486
+ XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
487
+
488
+ // attribute for type (only if typeHints => true)
489
+ XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
490
+
491
+ // attribute for class of objects (only if typeHints => true)
492
+ XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
493
+
494
+ // scalar values (strings, ints,..) will be serialized as attribute
495
+ XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false,
496
+
497
+ // prepend string for attributes
498
+ XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '',
499
+
500
+ // indent the attributes, if set to '_auto',
501
+ // it will indent attributes so they all start at the same column
502
+ XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false,
503
+
504
+ // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname
505
+ // if transforming an indexed array
506
+ XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT,
507
+
508
+ // add a doctype declaration
509
+ XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false,
510
+
511
+ // supply a string or an array with id and uri
512
+ // ({@see XML_Util::getDoctypeDeclaration()}
513
+ XML_SERIALIZER_OPTION_DOCTYPE => null,
514
+
515
+ // name of the root tag
516
+ XML_SERIALIZER_OPTION_ROOT_NAME => null,
517
+
518
+ // attributes of the root tag
519
+ XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(),
520
+
521
+ // all values in this key will be treated as attributes
522
+ XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null,
523
+
524
+ // this value will be used directly as content,
525
+ // instead of creating a new tag, may only be used
526
+ // in conjuction with attributesArray
527
+ XML_SERIALIZER_OPTION_CONTENT_KEY => null,
528
+
529
+ // this value will be used directly as comment,
530
+ // instead of creating a new tag, may only be used
531
+ // in conjuction with attributesArray
532
+ XML_SERIALIZER_OPTION_COMMENT_KEY => null,
533
+
534
+ // tag names that will be changed
535
+ XML_SERIALIZER_OPTION_TAGMAP => array(),
536
+
537
+ // function that will be applied before serializing
538
+ XML_SERIALIZER_OPTION_ENCODE_FUNC => null,
539
+
540
+ // namespace to use
541
+ XML_SERIALIZER_OPTION_NAMESPACE => null,
542
+
543
+ // type of entities to replace,
544
+ XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML,
545
+
546
+ // serialize() returns the result of the serialization instead of true
547
+ XML_SERIALIZER_OPTION_RETURN_RESULT => false,
548
+
549
+ // ignore properties that are set to null
550
+ XML_SERIALIZER_OPTION_IGNORE_NULL => false,
551
+
552
+ // Whether to use cdata sections for plain character data
553
+ XML_SERIALIZER_OPTION_CDATA_SECTIONS => false
554
+ );
555
+
556
+ /**
557
+ * options for the serialization
558
+ *
559
+ * @access public
560
+ * @var array
561
+ */
562
+ var $options = array();
563
+
564
+ /**
565
+ * current tag depth
566
+ *
567
+ * @access private
568
+ * @var integer
569
+ */
570
+ var $_tagDepth = 0;
571
+
572
+ /**
573
+ * serilialized representation of the data
574
+ *
575
+ * @access private
576
+ * @var string
577
+ */
578
+ var $_serializedData = null;
579
+
580
+ /**
581
+ * constructor
582
+ *
583
+ * @param mixed $options array containing options for the serialization
584
+ *
585
+ * @return void
586
+ * @access public
587
+ */
588
+ function XML_Serializer( $options = null )
589
+ {
590
+ $this->PEAR();
591
+ if (is_array($options)) {
592
+ $this->options = array_merge($this->_defaultOptions, $options);
593
+ } else {
594
+ $this->options = $this->_defaultOptions;
595
+ }
596
+ }
597
+
598
+ /**
599
+ * return API version
600
+ *
601
+ * @access public
602
+ * @static
603
+ * @return string $version API version
604
+ */
605
+ function apiVersion()
606
+ {
607
+ return '0.19.2';
608
+ }
609
+
610
+ /**
611
+ * reset all options to default options
612
+ *
613
+ * @return void
614
+ * @access public
615
+ * @see setOption(), XML_Serializer()
616
+ */
617
+ function resetOptions()
618
+ {
619
+ $this->options = $this->_defaultOptions;
620
+ }
621
+
622
+ /**
623
+ * set an option
624
+ *
625
+ * You can use this method if you do not want
626
+ * to set all options in the constructor
627
+ *
628
+ * @param string $name option name
629
+ * @param mixed $value option value
630
+ *
631
+ * @return void
632
+ * @access public
633
+ * @see resetOption(), XML_Serializer()
634
+ */
635
+ function setOption($name, $value)
636
+ {
637
+ $this->options[$name] = $value;
638
+ }
639
+
640
+ /**
641
+ * sets several options at once
642
+ *
643
+ * You can use this method if you do not want
644
+ * to set all options in the constructor
645
+ *
646
+ * @param array $options options array
647
+ *
648
+ * @return void
649
+ * @access public
650
+ * @see resetOption(), XML_Unserializer(), setOption()
651
+ */
652
+ function setOptions($options)
653
+ {
654
+ $this->options = array_merge($this->options, $options);
655
+ }
656
+
657
+ /**
658
+ * serialize data
659
+ *
660
+ * @param mixed $data data to serialize
661
+ * @param array $options options array
662
+ *
663
+ * @return boolean true on success, pear error on failure
664
+ * @access public
665
+ */
666
+ function serialize($data, $options = null)
667
+ {
668
+ // if options have been specified, use them instead
669
+ // of the previously defined ones
670
+ if (is_array($options)) {
671
+ $optionsBak = $this->options;
672
+ if (isset($options['overrideOptions'])
673
+ && $options['overrideOptions'] == true
674
+ ) {
675
+ $this->options = array_merge($this->_defaultOptions, $options);
676
+ } else {
677
+ $this->options = array_merge($this->options, $options);
678
+ }
679
+ } else {
680
+ $optionsBak = null;
681
+ }
682
+
683
+ // start depth is zero
684
+ $this->_tagDepth = 0;
685
+
686
+ $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
687
+ if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
688
+ && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])
689
+ ) {
690
+ $rootAttributes['xmlns:'
691
+ . $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] =
692
+ $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
693
+ }
694
+
695
+ $this->_serializedData = '';
696
+ // serialize an array
697
+ if (is_array($data)) {
698
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
699
+ $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
700
+ } else {
701
+ $tagName = 'array';
702
+ }
703
+
704
+ $this->_serializedData .=
705
+ $this->_serializeArray($data, $tagName, $rootAttributes);
706
+ } elseif (is_object($data)) {
707
+ // serialize an object
708
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
709
+ $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
710
+ } else {
711
+ $tagName = get_class($data);
712
+ }
713
+ $this->_serializedData .=
714
+ $this->_serializeObject($data, $tagName, $rootAttributes);
715
+ } else {
716
+ $tag = array();
717
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
718
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
719
+ } else {
720
+ $tag['qname'] = gettype($data);
721
+ }
722
+ $tagName = $tag['qname'];
723
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
724
+ $rootAttributes[$this->
725
+ options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
726
+ }
727
+ @settype($data, 'string');
728
+ $tag['content'] = $data;
729
+ $tag['attributes'] = $rootAttributes;
730
+ $this->_serializedData = $this->_createXMLTag($tag);
731
+ }
732
+
733
+ // add doctype declaration
734
+ if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
735
+ $this->_serializedData =
736
+ XML_Util::getDoctypeDeclaration($tagName,
737
+ $this->options[XML_SERIALIZER_OPTION_DOCTYPE])
738
+ . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
739
+ . $this->_serializedData;
740
+ }
741
+
742
+ // build xml declaration
743
+ if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
744
+ $atts = array();
745
+ $this->_serializedData = XML_Util::getXMLDeclaration('1.0',
746
+ $this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
747
+ . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
748
+ . $this->_serializedData;
749
+ }
750
+
751
+ if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
752
+ $result = $this->_serializedData;
753
+ } else {
754
+ $result = true;
755
+ }
756
+
757
+ if ($optionsBak !== null) {
758
+ $this->options = $optionsBak;
759
+ }
760
+
761
+ return $result;
762
+ }
763
+
764
+ /**
765
+ * get the result of the serialization
766
+ *
767
+ * @access public
768
+ * @return string serialized XML
769
+ */
770
+ function getSerializedData()
771
+ {
772
+ if ($this->_serializedData == null) {
773
+ return $this->raiseError('No serialized data available. '
774
+ . 'Use XML_Serializer::serialize() first.',
775
+ XML_SERIALIZER_ERROR_NO_SERIALIZATION);
776
+ }
777
+ return $this->_serializedData;
778
+ }
779
+
780
+ /**
781
+ * serialize any value
782
+ *
783
+ * This method checks for the type of the value and calls the appropriate method
784
+ *
785
+ * @param mixed $value tag value
786
+ * @param string $tagName tag name
787
+ * @param array $attributes attributes
788
+ *
789
+ * @return string
790
+ * @access private
791
+ */
792
+ function _serializeValue($value, $tagName = null, $attributes = array())
793
+ {
794
+ if (is_array($value)) {
795
+ $xml = $this->_serializeArray($value, $tagName, $attributes);
796
+ } elseif (is_object($value)) {
797
+ $xml = $this->_serializeObject($value, $tagName);
798
+ } else {
799
+ $tag = array(
800
+ 'qname' => $tagName,
801
+ 'attributes' => $attributes,
802
+ 'content' => $value
803
+ );
804
+ $xml = $this->_createXMLTag($tag);
805
+ }
806
+ return $xml;
807
+ }
808
+
809
+ /**
810
+ * serialize an array
811
+ *
812
+ * @param array &$array array to serialize
813
+ * @param string $tagName name of the root tag
814
+ * @param array $attributes attributes for the root tag
815
+ *
816
+ * @return string $string serialized data
817
+ * @access private
818
+ * @uses XML_Util::isValidName() to check, whether key has to be substituted
819
+ */
820
+ function _serializeArray(&$array, $tagName = null, $attributes = array())
821
+ {
822
+ $_content = null;
823
+ $_comment = null;
824
+
825
+ // check for comment
826
+ if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
827
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])
828
+ ) {
829
+ $_comment =
830
+ $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
831
+ unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
832
+ }
833
+ }
834
+
835
+ /**
836
+ * check for special attributes
837
+ */
838
+ if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
839
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])
840
+ ) {
841
+ $attributes =
842
+ $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
843
+ unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
844
+ }
845
+ /**
846
+ * check for special content
847
+ */
848
+ if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
849
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])
850
+ ) {
851
+ $_content =
852
+ XML_Util::replaceEntities($array
853
+ [$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
854
+ unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
855
+ }
856
+ }
857
+ }
858
+
859
+ if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
860
+ foreach (array_keys($array) as $key) {
861
+ if (is_null($array[$key])) {
862
+ unset($array[$key]);
863
+ }
864
+ }
865
+ }
866
+
867
+ /*
868
+ * if mode is set to simpleXML, check whether
869
+ * the array is associative or indexed
870
+ */
871
+ if (is_array($array) && !empty($array)
872
+ && $this->options[XML_SERIALIZER_OPTION_MODE]
873
+ == XML_SERIALIZER_MODE_SIMPLEXML
874
+ ) {
875
+ $indexed = true;
876
+ foreach ($array as $key => $val) {
877
+ if (!is_int($key)) {
878
+ $indexed = false;
879
+ break;
880
+ }
881
+ }
882
+
883
+ if ($indexed
884
+ && $this->options[XML_SERIALIZER_OPTION_MODE]
885
+ == XML_SERIALIZER_MODE_SIMPLEXML
886
+ ) {
887
+ $string = '';
888
+ foreach ($array as $key => $val) {
889
+ $string .= $this->_serializeValue($val, $tagName, $attributes);
890
+
891
+ $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
892
+ // do indentation
893
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
894
+ && $this->_tagDepth>0
895
+ ) {
896
+ $string .=
897
+ str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
898
+ $this->_tagDepth);
899
+ }
900
+ }
901
+ return rtrim($string);
902
+ }
903
+ }
904
+
905
+ $scalarAsAttributes = false;
906
+ if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES])
907
+ && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]
908
+ [$tagName])
909
+ ) {
910
+ $scalarAsAttributes =
911
+ $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
912
+ } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true
913
+ ) {
914
+ $scalarAsAttributes = true;
915
+ }
916
+
917
+ if ($scalarAsAttributes === true) {
918
+ $this->expectError('*');
919
+ foreach ($array as $key => $value) {
920
+ if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
921
+ unset($array[$key]);
922
+ $attributes[$this->options
923
+ [XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
924
+ }
925
+ }
926
+ $this->popExpect();
927
+ } elseif (is_array($scalarAsAttributes)) {
928
+ $this->expectError('*');
929
+ foreach ($scalarAsAttributes as $key) {
930
+ if (!isset($array[$key])) {
931
+ continue;
932
+ }
933
+ $value = $array[$key];
934
+ if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
935
+ unset($array[$key]);
936
+ $attributes[$this->options
937
+ [XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
938
+ }
939
+ }
940
+ $this->popExpect();
941
+ }
942
+
943
+ // check for empty array => create empty tag
944
+ if (empty($array)) {
945
+ $tag = array(
946
+ 'qname' => $tagName,
947
+ 'content' => $_content,
948
+ 'attributes' => $attributes
949
+ );
950
+ } else {
951
+ $this->_tagDepth++;
952
+ $tmp = $_content . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
953
+ foreach ($array as $key => $value) {
954
+ // do indentation
955
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
956
+ && $this->_tagDepth>0
957
+ ) {
958
+ $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
959
+ $this->_tagDepth);
960
+ }
961
+
962
+ // copy key
963
+ $origKey = $key;
964
+ $this->expectError('*');
965
+ // key cannot be used as tagname => use default tag
966
+ $valid = XML_Util::isValidName($key);
967
+ $this->popExpect();
968
+ if (PEAR::isError($valid)) {
969
+ if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME]
970
+ && is_object($value)
971
+ ) {
972
+ $key = get_class($value);
973
+ } else {
974
+ $key = $this->_getDefaultTagname($tagName);
975
+ }
976
+ }
977
+
978
+ // once we've established the true $key, is there a tagmap for it?
979
+ if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
980
+ $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
981
+ }
982
+
983
+ $atts = array();
984
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
985
+ $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] =
986
+ gettype($value);
987
+ if ($key !== $origKey) {
988
+ $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] =
989
+ (string)$origKey;
990
+ }
991
+ }
992
+
993
+ $tmp .= $this->_createXMLTag(array(
994
+ 'qname' => $key,
995
+ 'attributes' => $atts,
996
+ 'content' => $value
997
+ ));
998
+ $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
999
+ }
1000
+
1001
+ $this->_tagDepth--;
1002
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
1003
+ && $this->_tagDepth>0
1004
+ ) {
1005
+ $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1006
+ $this->_tagDepth);
1007
+ }
1008
+
1009
+ if (trim($tmp) === '') {
1010
+ $tmp = null;
1011
+ }
1012
+
1013
+ $tag = array(
1014
+ 'qname' => $tagName,
1015
+ 'content' => $tmp,
1016
+ 'attributes' => $attributes
1017
+ );
1018
+ }
1019
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
1020
+ if (!isset($tag['attributes']
1021
+ [$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])
1022
+ ) {
1023
+ $tag['attributes']
1024
+ [$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
1025
+ }
1026
+ }
1027
+
1028
+ $string = '';
1029
+ if (!is_null($_comment)) {
1030
+ $string .= XML_Util::createComment($_comment);
1031
+ $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
1032
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null
1033
+ && $this->_tagDepth>0
1034
+ ) {
1035
+ $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1036
+ $this->_tagDepth);
1037
+ }
1038
+ }
1039
+ $string .= $this->_createXMLTag($tag, false);
1040
+ return $string;
1041
+ }
1042
+
1043
+ /**
1044
+ * get the name of the default tag.
1045
+ *
1046
+ * The name of the parent tag needs to be passed as the
1047
+ * default name can depend on the context.
1048
+ *
1049
+ * @param string $parent name of the parent tag
1050
+ *
1051
+ * @return string default tag name
1052
+ */
1053
+ function _getDefaultTagname($parent)
1054
+ {
1055
+ if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
1056
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
1057
+ }
1058
+ if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
1059
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
1060
+ } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
1061
+ ['#default'])
1062
+ ) {
1063
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
1064
+ } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]
1065
+ ['__default'])
1066
+ ) {
1067
+ // keep this for BC
1068
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
1069
+ }
1070
+ return 'XML_Serializer_Tag';
1071
+ }
1072
+
1073
+ /**
1074
+ * serialize an object
1075
+ *
1076
+ * @param object &$object object to serialize
1077
+ * @param string $tagName tag name
1078
+ * @param array $attributes attributes
1079
+ *
1080
+ * @return string $string serialized data
1081
+ * @access private
1082
+ */
1083
+ function _serializeObject(&$object, $tagName = null, $attributes = array())
1084
+ {
1085
+ // check for magic function
1086
+ if (method_exists($object, '__sleep')) {
1087
+ $propNames = $object->__sleep();
1088
+ if (is_array($propNames)) {
1089
+ $properties = array();
1090
+ foreach ($propNames as $propName) {
1091
+ $properties[$propName] = $object->$propName;
1092
+ }
1093
+ } else {
1094
+ $properties = get_object_vars($object);
1095
+ }
1096
+ } else {
1097
+ $properties = get_object_vars($object);
1098
+ }
1099
+
1100
+ if (empty($tagName)) {
1101
+ $tagName = get_class($object);
1102
+ }
1103
+
1104
+ // typehints activated?
1105
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
1106
+ $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] =
1107
+ 'object';
1108
+ $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] =
1109
+ get_class($object);
1110
+ }
1111
+ $string = $this->_serializeArray($properties, $tagName, $attributes);
1112
+ return $string;
1113
+ }
1114
+
1115
+ /**
1116
+ * create a tag from an array
1117
+ * this method awaits an array in the following format
1118
+ * array(
1119
+ * 'qname' => $tagName,
1120
+ * 'attributes' => array(),
1121
+ * 'content' => $content, // optional
1122
+ * 'namespace' => $namespace // optional
1123
+ * 'namespaceUri' => $namespaceUri // optional
1124
+ * )
1125
+ *
1126
+ * @param array $tag tag definition
1127
+ * @param boolean $firstCall whether or not this is the first call
1128
+ *
1129
+ * @return string $string XML tag
1130
+ * @access private
1131
+ */
1132
+ function _createXMLTag($tag, $firstCall = true)
1133
+ {
1134
+ // build fully qualified tag name
1135
+ if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
1136
+ if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
1137
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]
1138
+ . ':' . $tag['qname'];
1139
+ } else {
1140
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE]
1141
+ . ':' . $tag['qname'];
1142
+ }
1143
+ }
1144
+
1145
+ // attribute indentation
1146
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
1147
+ $multiline = true;
1148
+ $indent = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT],
1149
+ $this->_tagDepth);
1150
+
1151
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
1152
+ $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
1153
+
1154
+ } else {
1155
+ $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
1156
+ }
1157
+ } else {
1158
+ $multiline = false;
1159
+ $indent = false;
1160
+ }
1161
+
1162
+ if (is_array($tag['content'])) {
1163
+ if (empty($tag['content'])) {
1164
+ $tag['content'] = '';
1165
+ }
1166
+ } elseif (is_scalar($tag['content']) && (string)$tag['content'] == '') {
1167
+ $tag['content'] = '';
1168
+ }
1169
+
1170
+ // replace XML entities
1171
+ if ($firstCall === true) {
1172
+ if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
1173
+ $replaceEntities = XML_UTIL_CDATA_SECTION;
1174
+ } else {
1175
+ $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
1176
+ }
1177
+ } else {
1178
+ // this is a nested call, so value is already encoded
1179
+ // and must not be encoded again
1180
+ $replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
1181
+ // but attributes need to be encoded anyways
1182
+ // (done here because the rest of the code assumes the same encoding
1183
+ // can be used both for attributes and content)
1184
+ foreach ($tag['attributes'] as $k => &$v) {
1185
+ $v = XML_Util::replaceEntities($v,
1186
+ $this->options[XML_SERIALIZER_OPTION_ENTITIES]);
1187
+ }
1188
+ }
1189
+ if (is_scalar($tag['content']) || is_null($tag['content'])) {
1190
+ if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
1191
+ if ($firstCall === true) {
1192
+ $tag['content'] = call_user_func($this->
1193
+ options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
1194
+ }
1195
+ $tag['attributes'] = array_map($this->
1196
+ options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
1197
+ }
1198
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline,
1199
+ $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
1200
+ } elseif (is_array($tag['content'])) {
1201
+ $tag = $this->_serializeArray($tag['content'], $tag['qname'],
1202
+ $tag['attributes']);
1203
+ } elseif (is_object($tag['content'])) {
1204
+ $tag = $this->_serializeObject($tag['content'], $tag['qname'],
1205
+ $tag['attributes']);
1206
+ } elseif (is_resource($tag['content'])) {
1207
+ settype($tag['content'], 'string');
1208
+ if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
1209
+ if ($replaceEntities === true) {
1210
+ $tag['content'] = call_user_func($this->
1211
+ options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
1212
+ }
1213
+ $tag['attributes'] = array_map($this->
1214
+ options[XML_SERIALIZER_OPTION_ENCODE_FUNC],
1215
+ $tag['attributes']);
1216
+ }
1217
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
1218
+ }
1219
+ return $tag;
1220
+ }
1221
+ }
1222
+ ?>
lib/PEAR/XML/Unserializer.php ADDED
@@ -0,0 +1,983 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?PHP
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * XML_Unserializer
6
+ *
7
+ * Parses any XML document into PHP data structures.
8
+ *
9
+ * PHP versions 4 and 5
10
+ *
11
+ * LICENSE:
12
+ *
13
+ * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
14
+ * All rights reserved.
15
+ *
16
+ * Redistribution and use in source and binary forms, with or without
17
+ * modification, are permitted provided that the following conditions
18
+ * are met:
19
+ *
20
+ * * Redistributions of source code must retain the above copyright
21
+ * notice, this list of conditions and the following disclaimer.
22
+ * * Redistributions in binary form must reproduce the above copyright
23
+ * notice, this list of conditions and the following disclaimer in the
24
+ * documentation and/or other materials provided with the distribution.
25
+ * * The name of the author may not be used to endorse or promote products
26
+ * derived from this software without specific prior written permission.
27
+ *
28
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
30
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
32
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
36
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ *
40
+ * @category XML
41
+ * @package XML_Serializer
42
+ * @author Stephan Schmidt <schst@php.net>
43
+ * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
44
+ * @license http://opensource.org/licenses/bsd-license New BSD License
45
+ * @version CVS: $Id: Unserializer.php,v 1.41 2008/08/25 00:07:16 ashnazg Exp $
46
+ * @link http://pear.php.net/package/XML_Serializer
47
+ * @see XML_Unserializer
48
+ */
49
+
50
+ /**
51
+ * uses PEAR error managemt
52
+ */
53
+ require_once 'PEAR.php';
54
+
55
+ /**
56
+ * uses XML_Parser to unserialize document
57
+ */
58
+ require_once 'XML/Parser.php';
59
+
60
+ /**
61
+ * option: Convert nested tags to array or object
62
+ *
63
+ * Possible values:
64
+ * - array
65
+ * - object
66
+ * - associative array to define this option per tag name
67
+ */
68
+ define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType');
69
+
70
+ /**
71
+ * option: Name of the attribute that stores the original key
72
+ *
73
+ * Possible values:
74
+ * - any string
75
+ */
76
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
77
+
78
+ /**
79
+ * option: Name of the attribute that stores the type
80
+ *
81
+ * Possible values:
82
+ * - any string
83
+ */
84
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
85
+
86
+ /**
87
+ * option: Name of the attribute that stores the class name
88
+ *
89
+ * Possible values:
90
+ * - any string
91
+ */
92
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
93
+
94
+ /**
95
+ * option: Whether to use the tag name as a class name
96
+ *
97
+ * Possible values:
98
+ * - true or false
99
+ */
100
+ define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass');
101
+
102
+ /**
103
+ * option: Name of the default class
104
+ *
105
+ * Possible values:
106
+ * - any string
107
+ */
108
+ define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass');
109
+
110
+ /**
111
+ * option: Whether to parse attributes
112
+ *
113
+ * Possible values:
114
+ * - true or false
115
+ */
116
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes');
117
+
118
+ /**
119
+ * option: Key of the array to store attributes (if any)
120
+ *
121
+ * Possible values:
122
+ * - any string
123
+ * - false (disabled)
124
+ */
125
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray');
126
+
127
+ /**
128
+ * option: string to prepend attribute name (if any)
129
+ *
130
+ * Possible values:
131
+ * - any string
132
+ * - false (disabled)
133
+ */
134
+ define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes');
135
+
136
+ /**
137
+ * option: key to store the content,
138
+ * if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used
139
+ *
140
+ * Possible values:
141
+ * - any string
142
+ */
143
+ define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName');
144
+
145
+ /**
146
+ * option: map tag names
147
+ *
148
+ * Possible values:
149
+ * - associative array
150
+ */
151
+ define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap');
152
+
153
+ /**
154
+ * option: list of tags that will always be enumerated
155
+ *
156
+ * Possible values:
157
+ * - indexed array
158
+ */
159
+ define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum');
160
+
161
+ /**
162
+ * option: Encoding of the XML document
163
+ *
164
+ * Possible values:
165
+ * - UTF-8
166
+ * - ISO-8859-1
167
+ */
168
+ define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding');
169
+
170
+ /**
171
+ * option: Desired target encoding of the data
172
+ *
173
+ * Possible values:
174
+ * - UTF-8
175
+ * - ISO-8859-1
176
+ */
177
+ define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding');
178
+
179
+ /**
180
+ * option: Callback that will be applied to textual data
181
+ *
182
+ * Possible values:
183
+ * - any valid PHP callback
184
+ */
185
+ define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction');
186
+
187
+ /**
188
+ * option: whether to return the result of the unserialization from unserialize()
189
+ *
190
+ * Possible values:
191
+ * - true
192
+ * - false (default)
193
+ */
194
+ define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
195
+
196
+ /**
197
+ * option: set the whitespace behaviour
198
+ *
199
+ * Possible values:
200
+ * - XML_UNSERIALIZER_WHITESPACE_KEEP
201
+ * - XML_UNSERIALIZER_WHITESPACE_TRIM
202
+ * - XML_UNSERIALIZER_WHITESPACE_NORMALIZE
203
+ */
204
+ define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace');
205
+
206
+ /**
207
+ * Keep all whitespace
208
+ */
209
+ define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep');
210
+
211
+ /**
212
+ * remove whitespace from start and end of the data
213
+ */
214
+ define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim');
215
+
216
+ /**
217
+ * normalize whitespace
218
+ */
219
+ define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize');
220
+
221
+ /**
222
+ * option: whether to ovverride all options that have been set before
223
+ *
224
+ * Possible values:
225
+ * - true
226
+ * - false (default)
227
+ */
228
+ define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions');
229
+
230
+ /**
231
+ * option: list of tags, that will not be used as keys
232
+ */
233
+ define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys');
234
+
235
+ /**
236
+ * option: whether to use type guessing for scalar values
237
+ */
238
+ define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes');
239
+
240
+ /**
241
+ * error code for no serialization done
242
+ */
243
+ define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151);
244
+
245
+ /**
246
+ * XML_Unserializer
247
+ *
248
+ * class to unserialize XML documents that have been created with
249
+ * XML_Serializer. To unserialize an XML document you have to add
250
+ * type hints to the XML_Serializer options.
251
+ *
252
+ * If no type hints are available, XML_Unserializer will guess how
253
+ * the tags should be treated, that means complex structures will be
254
+ * arrays and tags with only CData in them will be strings.
255
+ *
256
+ * <code>
257
+ * require_once 'XML/Unserializer.php';
258
+ *
259
+ * // be careful to always use the ampersand in front of the new operator
260
+ * $unserializer = &new XML_Unserializer();
261
+ *
262
+ * $unserializer->unserialize($xml);
263
+ *
264
+ * $data = $unserializer->getUnserializedData();
265
+ * <code>
266
+ *
267
+ * @category XML
268
+ * @package XML_Serializer
269
+ * @author Stephan Schmidt <schst@php.net>
270
+ * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
271
+ * @license http://opensource.org/licenses/bsd-license New BSD License
272
+ * @version Release: 0.19.2
273
+ * @link http://pear.php.net/package/XML_Serializer
274
+ * @see XML_Serializer
275
+ */
276
+ class XML_Unserializer extends PEAR
277
+ {
278
+ /**
279
+ * list of all available options
280
+ *
281
+ * @access private
282
+ * @var array
283
+ */
284
+ var $_knownOptions = array(
285
+ XML_UNSERIALIZER_OPTION_COMPLEXTYPE,
286
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY,
287
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE,
288
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS,
289
+ XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME,
290
+ XML_UNSERIALIZER_OPTION_DEFAULT_CLASS,
291
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE,
292
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY,
293
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND,
294
+ XML_UNSERIALIZER_OPTION_CONTENT_KEY,
295
+ XML_UNSERIALIZER_OPTION_TAG_MAP,
296
+ XML_UNSERIALIZER_OPTION_FORCE_ENUM,
297
+ XML_UNSERIALIZER_OPTION_ENCODING_SOURCE,
298
+ XML_UNSERIALIZER_OPTION_ENCODING_TARGET,
299
+ XML_UNSERIALIZER_OPTION_DECODE_FUNC,
300
+ XML_UNSERIALIZER_OPTION_RETURN_RESULT,
301
+ XML_UNSERIALIZER_OPTION_WHITESPACE,
302
+ XML_UNSERIALIZER_OPTION_IGNORE_KEYS,
303
+ XML_UNSERIALIZER_OPTION_GUESS_TYPES
304
+ );
305
+ /**
306
+ * default options for the serialization
307
+ *
308
+ * @access private
309
+ * @var array
310
+ */
311
+ var $_defaultOptions = array(
312
+ // complex types will be converted to arrays, if no type hint is given
313
+ XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array',
314
+
315
+ // get array key/property name from this attribute
316
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey',
317
+
318
+ // get type from this attribute
319
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type',
320
+
321
+ // get class from this attribute (if not given, use tag name)
322
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class',
323
+
324
+ // use the tagname as the classname
325
+ XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME => true,
326
+
327
+ // name of the class that is used to create objects
328
+ XML_UNSERIALIZER_OPTION_DEFAULT_CLASS => 'stdClass',
329
+
330
+ // parse the attributes of the tag into an array
331
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => false,
332
+
333
+ // parse them into sperate array (specify name of array here)
334
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false,
335
+
336
+ // prepend attribute names with this string
337
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND => '',
338
+
339
+ // put cdata found in a tag that has been converted
340
+ // to a complex type in this key
341
+ XML_UNSERIALIZER_OPTION_CONTENT_KEY => '_content',
342
+
343
+ // use this to map tagnames
344
+ XML_UNSERIALIZER_OPTION_TAG_MAP => array(),
345
+
346
+ // these tags will always be an indexed array
347
+ XML_UNSERIALIZER_OPTION_FORCE_ENUM => array(),
348
+
349
+ // specify the encoding character of the document to parse
350
+ XML_UNSERIALIZER_OPTION_ENCODING_SOURCE => null,
351
+
352
+ // specify the target encoding
353
+ XML_UNSERIALIZER_OPTION_ENCODING_TARGET => null,
354
+
355
+ // function used to decode data
356
+ XML_UNSERIALIZER_OPTION_DECODE_FUNC => null,
357
+
358
+ // unserialize() returns the result of the unserialization instead of true
359
+ XML_UNSERIALIZER_OPTION_RETURN_RESULT => false,
360
+
361
+ // remove whitespace around data
362
+ XML_UNSERIALIZER_OPTION_WHITESPACE => XML_UNSERIALIZER_WHITESPACE_TRIM,
363
+
364
+ // List of tags that will automatically be added to the parent,
365
+ // instead of adding a new key
366
+ XML_UNSERIALIZER_OPTION_IGNORE_KEYS => array(),
367
+
368
+ // Whether to use type guessing
369
+ XML_UNSERIALIZER_OPTION_GUESS_TYPES => false
370
+ );
371
+
372
+ /**
373
+ * current options for the serialization
374
+ *
375
+ * @access public
376
+ * @var array
377
+ */
378
+ var $options = array();
379
+
380
+ /**
381
+ * unserialized data
382
+ *
383
+ * @access private
384
+ * @var string
385
+ */
386
+ var $_unserializedData = null;
387
+
388
+ /**
389
+ * name of the root tag
390
+ *
391
+ * @access private
392
+ * @var string
393
+ */
394
+ var $_root = null;
395
+
396
+ /**
397
+ * stack for all data that is found
398
+ *
399
+ * @access private
400
+ * @var array
401
+ */
402
+ var $_dataStack = array();
403
+
404
+ /**
405
+ * stack for all values that are generated
406
+ *
407
+ * @access private
408
+ * @var array
409
+ */
410
+ var $_valStack = array();
411
+
412
+ /**
413
+ * current tag depth
414
+ *
415
+ * @access private
416
+ * @var int
417
+ */
418
+ var $_depth = 0;
419
+
420
+ /**
421
+ * XML_Parser instance
422
+ *
423
+ * @access private
424
+ * @var object XML_Parser
425
+ */
426
+ var $_parser = null;
427
+
428
+ /**
429
+ * constructor
430
+ *
431
+ * @param mixed $options array containing options for the unserialization
432
+ *
433
+ * @access public
434
+ */
435
+ function XML_Unserializer($options = null)
436
+ {
437
+ if (is_array($options)) {
438
+ $this->options = array_merge($this->_defaultOptions, $options);
439
+ } else {
440
+ $this->options = $this->_defaultOptions;
441
+ }
442
+ }
443
+
444
+ /**
445
+ * return API version
446
+ *
447
+ * @access public
448
+ * @return string $version API version
449
+ * @static
450
+ */
451
+ function apiVersion()
452
+ {
453
+ return '0.19.2';
454
+ }
455
+
456
+ /**
457
+ * reset all options to default options
458
+ *
459
+ * @return void
460
+ * @access public
461
+ * @see setOption(), XML_Unserializer(), setOptions()
462
+ */
463
+ function resetOptions()
464
+ {
465
+ $this->options = $this->_defaultOptions;
466
+ }
467
+
468
+ /**
469
+ * set an option
470
+ *
471
+ * You can use this method if you do not want
472
+ * to set all options in the constructor
473
+ *
474
+ * @param string $name name of option
475
+ * @param mixed $value value of option
476
+ *
477
+ * @return void
478
+ * @access public
479
+ * @see resetOption(), XML_Unserializer(), setOptions()
480
+ */
481
+ function setOption($name, $value)
482
+ {
483
+ $this->options[$name] = $value;
484
+ }
485
+
486
+ /**
487
+ * sets several options at once
488
+ *
489
+ * You can use this method if you do not want
490
+ * to set all options in the constructor
491
+ *
492
+ * @param array $options options array
493
+ *
494
+ * @return void
495
+ * @access public
496
+ * @see resetOption(), XML_Unserializer(), setOption()
497
+ */
498
+ function setOptions($options)
499
+ {
500
+ $this->options = array_merge($this->options, $options);
501
+ }
502
+
503
+ /**
504
+ * unserialize data
505
+ *
506
+ * @param mixed $data data to unserialize (string, filename or resource)
507
+ * @param boolean $isFile data should be treated as a file
508
+ * @param array $options options that will override
509
+ * the global options for this call
510
+ *
511
+ * @return boolean $success
512
+ * @access public
513
+ */
514
+ function unserialize($data, $isFile = false, $options = null)
515
+ {
516
+ $this->_unserializedData = null;
517
+ $this->_root = null;
518
+
519
+ // if options have been specified, use them instead
520
+ // of the previously defined ones
521
+ if (is_array($options)) {
522
+ $optionsBak = $this->options;
523
+ if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS])
524
+ && $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true
525
+ ) {
526
+ $this->options = array_merge($this->_defaultOptions, $options);
527
+ } else {
528
+ $this->options = array_merge($this->options, $options);
529
+ }
530
+ } else {
531
+ $optionsBak = null;
532
+ }
533
+
534
+ $this->_valStack = array();
535
+ $this->_dataStack = array();
536
+ $this->_depth = 0;
537
+
538
+ $this->_createParser();
539
+
540
+ if (is_string($data)) {
541
+ if ($isFile) {
542
+ $result = $this->_parser->setInputFile($data);
543
+ if (PEAR::isError($result)) {
544
+ return $result;
545
+ }
546
+ $result = $this->_parser->parse();
547
+ } else {
548
+ $result = $this->_parser->parseString($data, true);
549
+ }
550
+ } else {
551
+ $this->_parser->setInput($data);
552
+ $result = $this->_parser->parse();
553
+ }
554
+
555
+ if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) {
556
+ $return = $this->_unserializedData;
557
+ } else {
558
+ $return = true;
559
+ }
560
+
561
+ if ($optionsBak !== null) {
562
+ $this->options = $optionsBak;
563
+ }
564
+
565
+ if (PEAR::isError($result)) {
566
+ return $result;
567
+ }
568
+
569
+ return $return;
570
+ }
571
+
572
+ /**
573
+ * get the result of the serialization
574
+ *
575
+ * @access public
576
+ * @return string $serializedData
577
+ */
578
+ function getUnserializedData()
579
+ {
580
+ if ($this->_root === null) {
581
+ return $this->raiseError('No unserialized data available. '
582
+ . 'Use XML_Unserializer::unserialize() first.',
583
+ XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
584
+ }
585
+ return $this->_unserializedData;
586
+ }
587
+
588
+ /**
589
+ * get the name of the root tag
590
+ *
591
+ * @access public
592
+ * @return string $rootName
593
+ */
594
+ function getRootName()
595
+ {
596
+ if ($this->_root === null) {
597
+ return $this->raiseError('No unserialized data available. '
598
+ . 'Use XML_Unserializer::unserialize() first.',
599
+ XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
600
+ }
601
+ return $this->_root;
602
+ }
603
+
604
+ /**
605
+ * Start element handler for XML parser
606
+ *
607
+ * @param object $parser XML parser object
608
+ * @param string $element XML element
609
+ * @param array $attribs attributes of XML tag
610
+ *
611
+ * @return void
612
+ * @access private
613
+ */
614
+ function startHandler($parser, $element, $attribs)
615
+ {
616
+ if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])
617
+ ) {
618
+ $type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]];
619
+
620
+ $guessType = false;
621
+ } else {
622
+ $type = 'string';
623
+ if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
624
+ $guessType = true;
625
+ } else {
626
+ $guessType = false;
627
+ }
628
+ }
629
+
630
+ if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
631
+ $attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC],
632
+ $attribs);
633
+ }
634
+
635
+ $this->_depth++;
636
+ $this->_dataStack[$this->_depth] = null;
637
+
638
+ if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP])
639
+ && isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])
640
+ ) {
641
+ $element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element];
642
+ }
643
+
644
+ $val = array(
645
+ 'name' => $element,
646
+ 'value' => null,
647
+ 'type' => $type,
648
+ 'guessType' => $guessType,
649
+ 'childrenKeys' => array(),
650
+ 'aggregKeys' => array()
651
+ );
652
+
653
+ if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true
654
+ && (count($attribs) > 0)
655
+ ) {
656
+ $val['children'] = array();
657
+ $val['type'] = $this->_getComplexType($element);
658
+ $val['class'] = $element;
659
+
660
+ if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
661
+ $attribs = $this->_guessAndSetTypes($attribs);
662
+ }
663
+ if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false
664
+ ) {
665
+ $val['children'][$this->
666
+ options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs;
667
+ } else {
668
+ foreach ($attribs as $attrib => $value) {
669
+ $val['children'][$this->
670
+ options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND]
671
+ . $attrib] = $value;
672
+ }
673
+ }
674
+ }
675
+
676
+ $keyAttr = false;
677
+
678
+ if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
679
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY];
680
+ } elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
681
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
682
+ [$element])
683
+ ) {
684
+ $keyAttr =
685
+ $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element];
686
+ } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
687
+ ['#default'])
688
+ ) {
689
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
690
+ ['#default'];
691
+ } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
692
+ ['__default'])
693
+ ) {
694
+ // keep this for BC
695
+ $keyAttr =
696
+ $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]
697
+ ['__default'];
698
+ }
699
+ }
700
+
701
+ if ($keyAttr !== false && isset($attribs[$keyAttr])) {
702
+ $val['name'] = $attribs[$keyAttr];
703
+ }
704
+
705
+ if (isset($attribs[$this->
706
+ options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])
707
+ ) {
708
+ $val['class'] =
709
+ $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]];
710
+ }
711
+
712
+ array_push($this->_valStack, $val);
713
+ }
714
+
715
+ /**
716
+ * Try to guess the type of several values and
717
+ * set them accordingly
718
+ *
719
+ * @param array $array array containing the values
720
+ *
721
+ * @return array array, containing the values with their correct types
722
+ * @access private
723
+ */
724
+ function _guessAndSetTypes($array)
725
+ {
726
+ foreach ($array as $key => $value) {
727
+ $array[$key] = $this->_guessAndSetType($value);
728
+ }
729
+ return $array;
730
+ }
731
+
732
+ /**
733
+ * Try to guess the type of a value and
734
+ * set it accordingly
735
+ *
736
+ * @param string $value character data
737
+ *
738
+ * @return mixed value with the best matching type
739
+ * @access private
740
+ */
741
+ function _guessAndSetType($value)
742
+ {
743
+ if ($value === 'true') {
744
+ return true;
745
+ }
746
+ if ($value === 'false') {
747
+ return false;
748
+ }
749
+ if ($value === 'NULL') {
750
+ return null;
751
+ }
752
+ if (preg_match('/^[-+]?[0-9]{1,}$/', $value)) {
753
+ return intval($value);
754
+ }
755
+ if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}$/', $value)) {
756
+ return doubleval($value);
757
+ }
758
+ return (string)$value;
759
+ }
760
+
761
+ /**
762
+ * End element handler for XML parser
763
+ *
764
+ * @param object $parser XML parser object
765
+ * @param string $element element
766
+ *
767
+ * @return void
768
+ * @access private
769
+ */
770
+ function endHandler($parser, $element)
771
+ {
772
+ $value = array_pop($this->_valStack);
773
+ switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) {
774
+ case XML_UNSERIALIZER_WHITESPACE_KEEP:
775
+ $data = $this->_dataStack[$this->_depth];
776
+ break;
777
+ case XML_UNSERIALIZER_WHITESPACE_NORMALIZE:
778
+ $data = trim(preg_replace('/\s\s+/m', ' ',
779
+ $this->_dataStack[$this->_depth]));
780
+ break;
781
+ case XML_UNSERIALIZER_WHITESPACE_TRIM:
782
+ default:
783
+ $data = trim($this->_dataStack[$this->_depth]);
784
+ break;
785
+ }
786
+
787
+ // adjust type of the value
788
+ switch(strtolower($value['type'])) {
789
+
790
+ // unserialize an object
791
+ case 'object':
792
+ if (isset($value['class'])) {
793
+ $classname = $value['class'];
794
+ } else {
795
+ $classname = '';
796
+ }
797
+ // instantiate the class
798
+ if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true
799
+ && class_exists($classname)
800
+ ) {
801
+ $value['value'] = &new $classname;
802
+ } else {
803
+ $value['value'] =
804
+ &new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS];
805
+ }
806
+ if (trim($data) !== '') {
807
+ if ($value['guessType'] === true) {
808
+ $data = $this->_guessAndSetType($data);
809
+ }
810
+ $value['children'][$this->
811
+ options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
812
+ }
813
+
814
+ // set properties
815
+ foreach ($value['children'] as $prop => $propVal) {
816
+ // check whether there is a special method to set this property
817
+ $setMethod = 'set'.$prop;
818
+ if (method_exists($value['value'], $setMethod)) {
819
+ call_user_func(array(&$value['value'], $setMethod), $propVal);
820
+ } else {
821
+ $value['value']->$prop = $propVal;
822
+ }
823
+ }
824
+ // check for magic function
825
+ if (method_exists($value['value'], '__wakeup')) {
826
+ $value['value']->__wakeup();
827
+ }
828
+ break;
829
+
830
+ // unserialize an array
831
+ case 'array':
832
+ if (trim($data) !== '') {
833
+ if ($value['guessType'] === true) {
834
+ $data = $this->_guessAndSetType($data);
835
+ }
836
+ $value['children'][$this->
837
+ options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
838
+ }
839
+ if (isset($value['children'])) {
840
+ $value['value'] = $value['children'];
841
+ } else {
842
+ $value['value'] = array();
843
+ }
844
+ break;
845
+
846
+ // unserialize a null value
847
+ case 'null':
848
+ $data = null;
849
+ break;
850
+
851
+ // unserialize a resource => this is not possible :-(
852
+ case 'resource':
853
+ $value['value'] = $data;
854
+ break;
855
+
856
+ // unserialize any scalar value
857
+ default:
858
+ if ($value['guessType'] === true) {
859
+ $data = $this->_guessAndSetType($data);
860
+ } else {
861
+ settype($data, $value['type']);
862
+ }
863
+
864
+ $value['value'] = $data;
865
+ break;
866
+ }
867
+ $parent = array_pop($this->_valStack);
868
+ if ($parent === null) {
869
+ $this->_unserializedData = &$value['value'];
870
+ $this->_root = &$value['name'];
871
+ return true;
872
+ } else {
873
+ // parent has to be an array
874
+ if (!isset($parent['children']) || !is_array($parent['children'])) {
875
+ $parent['children'] = array();
876
+ if (!in_array($parent['type'], array('array', 'object'))) {
877
+ $parent['type'] = $this->_getComplexType($parent['name']);
878
+ if ($parent['type'] == 'object') {
879
+ $parent['class'] = $parent['name'];
880
+ }
881
+ }
882
+ }
883
+
884
+ if (in_array($element,
885
+ $this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])
886
+ ) {
887
+ $ignoreKey = true;
888
+ } else {
889
+ $ignoreKey = false;
890
+ }
891
+
892
+ if (!empty($value['name']) && $ignoreKey === false) {
893
+ // there already has been a tag with this name
894
+ if (in_array($value['name'], $parent['childrenKeys'])
895
+ || in_array($value['name'],
896
+ $this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])
897
+ ) {
898
+ // no aggregate has been created for this tag
899
+ if (!in_array($value['name'], $parent['aggregKeys'])) {
900
+ if (isset($parent['children'][$value['name']])) {
901
+ $parent['children'][$value['name']] =
902
+ array($parent['children'][$value['name']]);
903
+ } else {
904
+ $parent['children'][$value['name']] = array();
905
+ }
906
+ array_push($parent['aggregKeys'], $value['name']);
907
+ }
908
+ array_push($parent['children'][$value['name']], $value['value']);
909
+ } else {
910
+ $parent['children'][$value['name']] = &$value['value'];
911
+ array_push($parent['childrenKeys'], $value['name']);
912
+ }
913
+ } else {
914
+ array_push($parent['children'], $value['value']);
915
+ }
916
+ array_push($this->_valStack, $parent);
917
+ }
918
+
919
+ $this->_depth--;
920
+ }
921
+
922
+ /**
923
+ * Handler for character data
924
+ *
925
+ * @param object $parser XML parser object
926
+ * @param string $cdata CDATA
927
+ *
928
+ * @return void
929
+ * @access private
930
+ */
931
+ function cdataHandler($parser, $cdata)
932
+ {
933
+ if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
934
+ $cdata = call_user_func($this->
935
+ options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata);
936
+ }
937
+ $this->_dataStack[$this->_depth] .= $cdata;
938
+ }
939
+
940
+ /**
941
+ * get the complex type, that should be used for a specified tag
942
+ *
943
+ * @param string $tagname name of the tag
944
+ *
945
+ * @return string complex type ('array' or 'object')
946
+ * @access private
947
+ */
948
+ function _getComplexType($tagname)
949
+ {
950
+ if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) {
951
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE];
952
+ }
953
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) {
954
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname];
955
+ }
956
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) {
957
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'];
958
+ }
959
+ return 'array';
960
+ }
961
+
962
+ /**
963
+ * create the XML_Parser instance
964
+ *
965
+ * @return boolean
966
+ * @access private
967
+ */
968
+ function _createParser()
969
+ {
970
+ if (is_object($this->_parser)) {
971
+ $this->_parser->free();
972
+ unset($this->_parser);
973
+ }
974
+ $this->_parser = &new XML_Parser($this->
975
+ options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE],
976
+ 'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]);
977
+
978
+ $this->_parser->folding = false;
979
+ $this->_parser->setHandlerObj($this);
980
+ return true;
981
+ }
982
+ }
983
+ ?>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Lib_PEAR</name>
4
+ <version>1.3.1</version>
5
+ <stability>stable</stability>
6
+ <license>Mixed</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>PEAR Library</summary>
10
+ <description>PEAR Library</description>
11
+ <notes>1.3.1</notes>
12
+ <authors><author><name>Magento Core Team</name><user>auto-converted</user><email>core@magentocommerce.com</email></author></authors>
13
+ <date>2009-04-18</date>
14
+ <time>01:26:32</time>
15
+ <contents><target name="magelib"><dir name="PEAR"><dir name="HTTP"><dir name="Request"><file name="Listener.php" hash="77b53a700acc70a55713efd0642bc28a"/></dir><file name="HTTP.php" hash="b6408ededd0ec0d585b84951b7054810"/><file name="Request.php" hash="e409fdbe1518e11e16943f5185eccc2b"/></dir><dir name="Mail"><file name="mime.php" hash="93a00275edb0d9e94364ae11175741d1"/><file name="mimeDecode.php" hash="0e3cc74c0631a0e99e861caab50d763b"/><file name="mimePart.php" hash="53aedbcc104a9bd15cf96e5511b6498b"/><file name="xmail.dtd" hash="194810c478066eaeb28f51116b88e25a"/><file name="xmail.xsl" hash="61cea06fb6b4bd3a4b5e2d37384e14a9"/></dir><dir name="Net"><file name="Socket.php" hash="ea6a2a5f9ea70bc5ef25f03b0234cc08"/><file name="URL.php" hash="e7042d4176244ed99fe4b9c8e8cc6fb5"/></dir><dir name="PEAR"><file name="PEAR.php" hash="bd7b708d096f50fc873a2079fe03fd04"/></dir><dir name="SOAP"><dir name="Transport"><file name="HTTP.php" hash="7f3b3f8680fc1a337a6992056e6d41f4"/></dir><file name="Base.php" hash="7320b423fcdfb397c6a99cb7c585ac62"/><file name="Client.php" hash="1dd525ee520007e2d7651231c0dfefe3"/><file name="Fault.php" hash="34b9f405ebde848809b27ca2b16f1095"/><file name="Parser.php" hash="5c01a59b754194453a04fe24adb43ca4"/><file name="Transport.php" hash="0429fcddac603adc76c34c413705bd86"/><file name="Value.php" hash="026bce92aa9f922f0d8b410ff0f7ae3b"/><file name="WSDL.php" hash="67394dd9431b6970eac9a32df6171f2a"/></dir><dir name="XML"><dir name="Parser"><file name="Simple.php" hash="d11a9e750faecbfba6cc0c7a9eee5dcb"/></dir><file name="Parser.php" hash="f457617bc7ab0002f7374e9cca379681"/><file name="Serializer.php" hash="3fc9cbf6dff9b95372d05cb63854984d"/><file name="Unserializer.php" hash="937ba82a91ee40fa5a34911e70efaf3a"/></dir><file name="PEAR.php" hash="bd7b708d096f50fc873a2079fe03fd04"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies/>
18
+ </package>