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