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