1<?php
2/*
3
4NuSOAP - Web Services Toolkit for PHP
5
6Copyright (c) 2002 NuSphere Corporation
7
8This library is free software; you can redistribute it and/or
9modify it under the terms of the GNU Lesser General Public
10License as published by the Free Software Foundation; either
11version 2.1 of the License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16Lesser General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19along with this program. If not, see <https://www.gnu.org/licenses/>.
20
21The NuSOAP project home is:
22http://sourceforge.net/projects/nusoap/
23
24The primary support for NuSOAP is the mailing list:
25nusoap-general@lists.sourceforge.net
26
27If you have any questions or comments, please email:
28
29Dietrich Ayala
30dietrich@ganx4.com
31http://dietrich.ganx4.com/nusoap
32
33NuSphere Corporation
34http://www.nusphere.com
35
36*/
37
38/*require_once('nusoap.php');*/
39/* PEAR Mail_MIME library */
40require_once('Mail/mimeDecode.php');
41require_once('Mail/mimePart.php');
42
43/**
44* nusoap_client_mime client supporting MIME attachments defined at
45* http://www.w3.org/TR/SOAP-attachments.  It depends on the PEAR Mail_MIME library.
46*
47* @author   Scott Nichol <snichol@users.sourceforge.net>
48* @author	Thanks to Guillaume and Henning Reich for posting great attachment code to the mail list
49* @access   public
50*/
51class nusoap_client_mime extends nusoap_client {
52	/**
53	 * @var array Each array element in the return is an associative array with keys
54	 * data, filename, contenttype, cid
55	 * @access private
56	 */
57	var $requestAttachments = array();
58	/**
59	 * @var array Each array element in the return is an associative array with keys
60	 * data, filename, contenttype, cid
61	 * @access private
62	 */
63	var $responseAttachments;
64	/**
65	 * @var string
66	 * @access private
67	 */
68	var $mimeContentType;
69
70	/**
71	* adds a MIME attachment to the current request.
72	*
73	* If the $data parameter contains an empty string, this method will read
74	* the contents of the file named by the $filename parameter.
75	*
76	* If the $cid parameter is false, this method will generate the cid.
77	*
78	* @param string $data The data of the attachment
79	* @param string $filename The filename of the attachment (default is empty string)
80	* @param string $contenttype The MIME Content-Type of the attachment (default is application/octet-stream)
81	* @param string $cid The content-id (cid) of the attachment (default is false)
82	* @return string The content-id (cid) of the attachment
83	* @access public
84	*/
85	function addAttachment($data, $filename = '', $contenttype = 'application/octet-stream', $cid = false) {
86		if (! $cid) {
87			$cid = md5(uniqid(time()));
88		}
89
90		$info['data'] = $data;
91		$info['filename'] = $filename;
92		$info['contenttype'] = $contenttype;
93		$info['cid'] = $cid;
94
95		$this->requestAttachments[] = $info;
96
97		return $cid;
98	}
99
100	/**
101	* clears the MIME attachments for the current request.
102	*
103	* @access public
104	*/
105	function clearAttachments() {
106		$this->requestAttachments = array();
107	}
108
109	/**
110	* gets the MIME attachments from the current response.
111	*
112	* Each array element in the return is an associative array with keys
113	* data, filename, contenttype, cid.  These keys correspond to the parameters
114	* for addAttachment.
115	*
116	* @return array The attachments.
117	* @access public
118	*/
119	function getAttachments() {
120		return $this->responseAttachments;
121	}
122
123	/**
124	* gets the HTTP body for the current request.
125	*
126	* @param string $soapmsg The SOAP payload
127	* @return string The HTTP body, which includes the SOAP payload
128	* @access private
129	*/
130	function getHTTPBody($soapmsg) {
131		if (count($this->requestAttachments) > 0) {
132			$params['content_type'] = 'multipart/related; type="text/xml"';
133			$mimeMessage = new Mail_mimePart('', $params);
134			unset($params);
135
136			$params['content_type'] = 'text/xml';
137			$params['encoding']     = '8bit';
138			$params['charset']      = $this->soap_defencoding;
139			$mimeMessage->addSubpart($soapmsg, $params);
140
141			foreach ($this->requestAttachments as $att) {
142				unset($params);
143
144				$params['content_type'] = $att['contenttype'];
145				$params['encoding']     = 'base64';
146				$params['disposition']  = 'attachment';
147				$params['dfilename']    = $att['filename'];
148				$params['cid']          = $att['cid'];
149
150				if ($att['data'] == '' && $att['filename'] <> '') {
151					if ($fd = fopen($att['filename'], 'rb')) {
152						$data = fread($fd, filesize($att['filename']));
153						fclose($fd);
154					} else {
155						$data = '';
156					}
157					$mimeMessage->addSubpart($data, $params);
158				} else {
159					$mimeMessage->addSubpart($att['data'], $params);
160				}
161			}
162
163			$output = $mimeMessage->encode();
164			$mimeHeaders = $output['headers'];
165
166			foreach ($mimeHeaders as $k => $v) {
167				$this->debug("MIME header $k: $v");
168				if (strtolower($k) == 'content-type') {
169					// PHP header() seems to strip leading whitespace starting
170					// the second line, so force everything to one line
171					$this->mimeContentType = str_replace("\r\n", " ", $v);
172				}
173			}
174
175			return $output['body'];
176		}
177
178		return parent::getHTTPBody($soapmsg);
179	}
180
181	/**
182	* gets the HTTP content type for the current request.
183	*
184	* Note: getHTTPBody must be called before this.
185	*
186	* @return string the HTTP content type for the current request.
187	* @access private
188	*/
189	function getHTTPContentType() {
190		if (count($this->requestAttachments) > 0) {
191			return $this->mimeContentType;
192		}
193		return parent::getHTTPContentType();
194	}
195
196	/**
197	* gets the HTTP content type charset for the current request.
198	* returns false for non-text content types.
199	*
200	* Note: getHTTPBody must be called before this.
201	*
202	* @return string the HTTP content type charset for the current request.
203	* @access private
204	*/
205	function getHTTPContentTypeCharset() {
206		if (count($this->requestAttachments) > 0) {
207			return false;
208		}
209		return parent::getHTTPContentTypeCharset();
210	}
211
212	/**
213	* processes SOAP message returned from server
214	*
215	* @param	array	$headers	The HTTP headers
216	* @param	string	$data		unprocessed response data from server
217	* @return	mixed	value of the message, decoded into a PHP type
218	* @access   private
219	*/
220    function parseResponse($headers, $data) {
221		$this->debug('Entering parseResponse() for payload of length ' . strlen($data) . ' and type of ' . $headers['content-type']);
222		$this->responseAttachments = array();
223		if (strstr($headers['content-type'], 'multipart/related')) {
224			$this->debug('Decode multipart/related');
225			$input = '';
226			foreach ($headers as $k => $v) {
227				$input .= "$k: $v\r\n";
228			}
229			$params['input'] = $input . "\r\n" . $data;
230			$params['include_bodies'] = true;
231			$params['decode_bodies'] = true;
232			$params['decode_headers'] = true;
233
234			$structure = Mail_mimeDecode::decode($params);
235
236			foreach ($structure->parts as $part) {
237				if (!isset($part->disposition) && (strstr($part->headers['content-type'], 'text/xml'))) {
238					$this->debug('Have root part of type ' . $part->headers['content-type']);
239					$root = $part->body;
240					$return = parent::parseResponse($part->headers, $part->body);
241				} else {
242					$this->debug('Have an attachment of type ' . $part->headers['content-type']);
243					$info['data'] = $part->body;
244					$info['filename'] = isset($part->d_parameters['filename']) ? $part->d_parameters['filename'] : '';
245					$info['contenttype'] = $part->headers['content-type'];
246					$info['cid'] = $part->headers['content-id'];
247					$this->responseAttachments[] = $info;
248				}
249			}
250
251			if (isset($return)) {
252				$this->responseData = $root;
253				return $return;
254			}
255
256			$this->setError('No root part found in multipart/related content');
257			return '';
258		}
259		$this->debug('Not multipart/related');
260		return parent::parseResponse($headers, $data);
261	}
262}
263
264/*
265 *	For backwards compatiblity, define soapclientmime unless the PHP SOAP extension is loaded.
266 */
267if (!extension_loaded('soap')) {
268	class soapclientmime extends nusoap_client_mime {
269	}
270}
271
272/**
273* nusoap_server_mime server supporting MIME attachments defined at
274* http://www.w3.org/TR/SOAP-attachments.  It depends on the PEAR Mail_MIME library.
275*
276* @author   Scott Nichol <snichol@users.sourceforge.net>
277* @author	Thanks to Guillaume and Henning Reich for posting great attachment code to the mail list
278* @access   public
279*/
280class nusoap_server_mime extends nusoap_server {
281	/**
282	 * @var array Each array element in the return is an associative array with keys
283	 * data, filename, contenttype, cid
284	 * @access private
285	 */
286	var $requestAttachments = array();
287	/**
288	 * @var array Each array element in the return is an associative array with keys
289	 * data, filename, contenttype, cid
290	 * @access private
291	 */
292	var $responseAttachments;
293	/**
294	 * @var string
295	 * @access private
296	 */
297	var $mimeContentType;
298
299	/**
300	* adds a MIME attachment to the current response.
301	*
302	* If the $data parameter contains an empty string, this method will read
303	* the contents of the file named by the $filename parameter.
304	*
305	* If the $cid parameter is false, this method will generate the cid.
306	*
307	* @param string $data The data of the attachment
308	* @param string $filename The filename of the attachment (default is empty string)
309	* @param string $contenttype The MIME Content-Type of the attachment (default is application/octet-stream)
310	* @param string $cid The content-id (cid) of the attachment (default is false)
311	* @return string The content-id (cid) of the attachment
312	* @access public
313	*/
314	function addAttachment($data, $filename = '', $contenttype = 'application/octet-stream', $cid = false) {
315		if (! $cid) {
316			$cid = md5(uniqid(time()));
317		}
318
319		$info['data'] = $data;
320		$info['filename'] = $filename;
321		$info['contenttype'] = $contenttype;
322		$info['cid'] = $cid;
323
324		$this->responseAttachments[] = $info;
325
326		return $cid;
327	}
328
329	/**
330	* clears the MIME attachments for the current response.
331	*
332	* @access public
333	*/
334	function clearAttachments() {
335		$this->responseAttachments = array();
336	}
337
338	/**
339	* gets the MIME attachments from the current request.
340	*
341	* Each array element in the return is an associative array with keys
342	* data, filename, contenttype, cid.  These keys correspond to the parameters
343	* for addAttachment.
344	*
345	* @return array The attachments.
346	* @access public
347	*/
348	function getAttachments() {
349		return $this->requestAttachments;
350	}
351
352	/**
353	* gets the HTTP body for the current response.
354	*
355	* @param string $soapmsg The SOAP payload
356	* @return string The HTTP body, which includes the SOAP payload
357	* @access private
358	*/
359	function getHTTPBody($soapmsg) {
360		if (count($this->responseAttachments) > 0) {
361			$params['content_type'] = 'multipart/related; type="text/xml"';
362			$mimeMessage = new Mail_mimePart('', $params);
363			unset($params);
364
365			$params['content_type'] = 'text/xml';
366			$params['encoding']     = '8bit';
367			$params['charset']      = $this->soap_defencoding;
368			$mimeMessage->addSubpart($soapmsg, $params);
369
370			foreach ($this->responseAttachments as $att) {
371				unset($params);
372
373				$params['content_type'] = $att['contenttype'];
374				$params['encoding']     = 'base64';
375				$params['disposition']  = 'attachment';
376				$params['dfilename']    = $att['filename'];
377				$params['cid']          = $att['cid'];
378
379				if ($att['data'] == '' && $att['filename'] <> '') {
380					if ($fd = fopen($att['filename'], 'rb')) {
381						$data = fread($fd, filesize($att['filename']));
382						fclose($fd);
383					} else {
384						$data = '';
385					}
386					$mimeMessage->addSubpart($data, $params);
387				} else {
388					$mimeMessage->addSubpart($att['data'], $params);
389				}
390			}
391
392			$output = $mimeMessage->encode();
393			$mimeHeaders = $output['headers'];
394
395			foreach ($mimeHeaders as $k => $v) {
396				$this->debug("MIME header $k: $v");
397				if (strtolower($k) == 'content-type') {
398					// PHP header() seems to strip leading whitespace starting
399					// the second line, so force everything to one line
400					$this->mimeContentType = str_replace("\r\n", " ", $v);
401				}
402			}
403
404			return $output['body'];
405		}
406
407		return parent::getHTTPBody($soapmsg);
408	}
409
410	/**
411	* gets the HTTP content type for the current response.
412	*
413	* Note: getHTTPBody must be called before this.
414	*
415	* @return string the HTTP content type for the current response.
416	* @access private
417	*/
418	function getHTTPContentType() {
419		if (count($this->responseAttachments) > 0) {
420			return $this->mimeContentType;
421		}
422		return parent::getHTTPContentType();
423	}
424
425	/**
426	* gets the HTTP content type charset for the current response.
427	* returns false for non-text content types.
428	*
429	* Note: getHTTPBody must be called before this.
430	*
431	* @return string the HTTP content type charset for the current response.
432	* @access private
433	*/
434	function getHTTPContentTypeCharset() {
435		if (count($this->responseAttachments) > 0) {
436			return false;
437		}
438		return parent::getHTTPContentTypeCharset();
439	}
440
441	/**
442	* processes SOAP message received from client
443	*
444	* @param	array	$headers	The HTTP headers
445	* @param	string	$data		unprocessed request data from client
446	* @return	mixed	value of the message, decoded into a PHP type
447	* @access   private
448	*/
449    function parseRequest($headers, $data) {
450		$this->debug('Entering parseRequest() for payload of length ' . strlen($data) . ' and type of ' . $headers['content-type']);
451		$this->requestAttachments = array();
452		if (strstr($headers['content-type'], 'multipart/related')) {
453			$this->debug('Decode multipart/related');
454			$input = '';
455			foreach ($headers as $k => $v) {
456				$input .= "$k: $v\r\n";
457			}
458			$params['input'] = $input . "\r\n" . $data;
459			$params['include_bodies'] = true;
460			$params['decode_bodies'] = true;
461			$params['decode_headers'] = true;
462
463			$structure = Mail_mimeDecode::decode($params);
464
465			foreach ($structure->parts as $part) {
466				if (!isset($part->disposition) && (strstr($part->headers['content-type'], 'text/xml'))) {
467					$this->debug('Have root part of type ' . $part->headers['content-type']);
468					$return = parent::parseRequest($part->headers, $part->body);
469				} else {
470					$this->debug('Have an attachment of type ' . $part->headers['content-type']);
471					$info['data'] = $part->body;
472					$info['filename'] = isset($part->d_parameters['filename']) ? $part->d_parameters['filename'] : '';
473					$info['contenttype'] = $part->headers['content-type'];
474					$info['cid'] = $part->headers['content-id'];
475					$this->requestAttachments[] = $info;
476				}
477			}
478
479			if (isset($return)) {
480				return $return;
481			}
482
483			$this->setError('No root part found in multipart/related content');
484			return;
485		}
486		$this->debug('Not multipart/related');
487		return parent::parseRequest($headers, $data);
488	}
489}
490
491/*
492 *	For backwards compatiblity
493 */
494class nusoapservermime extends nusoap_server_mime {
495}
496
497?>
498