* * * * * * * * * * SOAP server in PHP: * * * 1)); * $server = new SoapServer(null,array('uri' => $_SERVER['REQUEST_URI'])); // non-WDSL mode * $server->addFunction('updateFile'); * if($mpI->isMultiPart()) { * $server->handle(substr($HTTP_RAW_POST_DATA, $mpI->index[0]['begin'], $mpI->index[0]['length'])); * } else { * $server->handle(); * } * * function updateFile($input) { * global $mpI; * global $HTTP_RAW_POST_DATA; * if($mpI->isMultiPart()) { * // retrieve first attachment and save it to the directory 'cache' * $mpI->loadMultiPartIndex(1); * file_put_contents('cache/PNGfileWhichWillBeUpdated.png', * substr($HTTP_RAW_POST_DATA, $mpI->index[1]['begin'], $mpI->index[1]['length'])); * $mpI->fetchMultiPartHeaders(); // run it only if you need parts headers * } * error_log( var_export(array('inputData' => $input, 'HTTPheadersOf1stAttachment' => $mpI->index[1]['headers']), true) ); * return(array('return' => 'true')); * } * * ?> * * * @see http://php.net/manual/en/class.soapserver.php PHP's SoapServer * @since 2012-08-24 * @version 2013-12-19 * @author Juks */ class MultipartRawPostHelper { /** * Parameters, which can be overwritten by constructor * * @see MultipartRawPostHelper::__construct() * @var array */ public $mutables = array( 'loadNextParts' => 0, 'newLine' => "\r\n" ); /** * Index, describing position of different parts in multipart message, one row per part * * Keys of elements inside row * * boundaryBegin : index of multipart boundary, preceding part's HTTP headers and body * * begin : index of body start * * end : index of body end * * length : end - begin * * headers : part's HTTP headers, available after running fetchMultiPartHeaders() * * @see MultipartRawPostHelper::__construct() * @see MultipartRawPostHelper::fetchMultiPartHeaders() * @var array */ public $index = array(); /** * @var string */ public $multipartBoundary = ''; /** * True, if multipart message is indexed, there's no more un-indexed parts * * @var boolean */ public $isIndexCompleted = false; /** * How many parts are already indexed? * * @var boolean */ public $indexLength = 0; /** * Constructor * * @param array To load first x part's indexes during init, use array('loadNextParts'=>x) * @return self * @see MultipartRawPostHelper::$mutable */ function __construct($init = array()) { $this->mutables = array_merge($this->mutables,$init); foreach(apache_request_headers() as $k=>$v) { if(strtoupper($k) == 'CONTENT-TYPE') { $matches = array(); if(preg_match('/^multipart\/related;.*boundary="?([^"]+)"?/', $v, $matches)) { $this->multipartBoundary = '--'.$matches[1]; } break; } } $this->loadMultiPartIndex($this->mutables['loadNextParts']); } /** * Is current message multipart message or not? * * @return boolean */ function isMultiPart() { return $this->multipartBoundary != ''; } /** * Load next $loadNextParts part's indexes * * @param int How many next parts will be indexed * @return void */ function loadMultiPartIndex($loadNextParts) { if($this->isMultiPart()) { for($i=0; $i<$loadNextParts; $i++) { if(!$this->indexNextMultiPart()) { break; } } } } /** * Calculate index for next part * * @see MultipartRawPostHelper::$index * @return boolean True, when indexing succeeded */ function indexNextMultiPart() { if($this->isIndexCompleted) { return false; } // do not copy or extract any parts of $HTTP_RAW_POST_DATA due memory concerns global $HTTP_RAW_POST_DATA; $currentIndex = count($this->index); $this->index[] = array(); // find first boundary's beginning by boundary string, because you cannot be sure how many whitespaces precede it if($currentIndex == 0) { $this->index[$currentIndex]['boundaryBegin'] = strpos($HTTP_RAW_POST_DATA, $this->multipartBoundary, 0); if($this->index[$currentIndex]['boundaryBegin'] === false) { $this->isIndexCompleted = true; return false; } } else { $this->index[$currentIndex]['boundaryBegin'] = $this->index[$currentIndex-1]['end'] + strlen($this->mutables['newLine']); } // part's beginning is after boundary + part's header(s) + double newline $this->index[$currentIndex]['begin'] = strpos($HTTP_RAW_POST_DATA, $this->mutables['newLine'].$this->mutables['newLine'], $this->index[$currentIndex]['boundaryBegin'] + strlen($this->multipartBoundary)); if($this->index[$currentIndex]['begin'] === false) { $this->isIndexCompleted = true; return false; } $this->index[$currentIndex]['begin'] += 2*strlen($this->mutables['newLine']); $this->index[$currentIndex]['end'] = strpos($HTTP_RAW_POST_DATA, $this->multipartBoundary, $this->index[$currentIndex]['begin']); if($this->index[$currentIndex]['end'] === false) { $this->isIndexCompleted = true; return false; } $this->index[$currentIndex]['end'] -= strlen($this->mutables['newLine']); $this->indexLength++; $this->index[$currentIndex]['length'] = $this->index[$currentIndex]['end'] - $this->index[$currentIndex]['begin']; return true; } /** * Fetch part's headers * * Generates array of part's internal HTTP headers into index row with access key 'headers' * NB! Part must be already indexed! * * @see MultipartRawPostHelper::$index * @return void */ function fetchMultiPartHeaders() { global $HTTP_RAW_POST_DATA; $i = 0; while( $i < count($this->index) && !isset($this->index[$i]['headers']) && isset($this->index[$i]['boundaryBegin']) && isset($this->index[$i]['begin']) ) { $this->index[$i]['headers'] = array(); $headers_start = $this->index[$i]['boundaryBegin'] + strlen($this->multipartBoundary); foreach(explode($this->mutables['newLine'],substr($HTTP_RAW_POST_DATA, $headers_start, $this->index[$i]['begin'] -$headers_start)) as $header) { $header = trim($header); if($header != '') { $this->index[$i]['headers'][] = $header; } } $i++; } } }