WSCOMUN  2.1.0
Web Services Comunes para PHP/GVHidra
WSCMimeParser.php
1 <?php
2 namespace WSCMIME;
10 {
11  const WSC_CHARSET = 'UTF-8';
17  protected $params = array(
18  'include_bodies' => true,
19  'decode_bodies' => true,
20  'decode_headers' => true,
21  'crlf' => "\r\n",
22  'default_charset' => self::WSC_CHARSET,
23  );
24 
25 
26  /*
27  * Si es false, no trata MTOM en profundidad (Uso interno)
28  */
29  private $allowMTOMPart = true;
30 
46  public function __construct($params = array())
47  {
48  if (!empty($params)) {
49  $this->params = array_merge($this->params, (array) $params);
50  }
51  }
60  public function decode($input, $convert = true)
61  {
62  list($header, $body) = $this->splitBodyHeader($input);
63  $struct = $this->doDecode($header, $body);
64  if ($struct && $convert) {
65  $struct = $this->structure_part($struct);
66  }
67  return $struct;
68  }
69 
70 
71 
80  public function decodeMTOM($vData, $convert = true)
81  {
82  $retorno = new \stdClass;
83  for ($iPart = 1; $iPart < count($vData)-1; $iPart++)
84  {
85  list($part_header, $part_body) = $this->splitBodyHeader( $vData[$iPart] );
86  $retorno->parts[] = $this->doDecode($part_header, $part_body);
87  }
88  $retorno->body = null;
89 
90  $struct = $retorno;
91  if ($struct && $convert)
92  {
93  $struct = $this->structure_part($struct);
94  }
95 
96  return $struct;
97  }//Fin decodeMTOM
98 
99 
111  protected function doDecode($headers, $body, $default_ctype = 'text/plain')
112  {
113  $regs = array();
114  $return = new \stdClass;
115  $headers = $this->parseHeaders($headers);
116  while (list(, $value) = each($headers)) {
117  $header_name = strtolower($value['name']);
118  if (isset($return->headers[$header_name]) && !is_array($return->headers[$header_name])) {
119  $return->headers[$header_name] = array($return->headers[$header_name]);
120  $return->headers[$header_name][] = $value['value'];
121  }
122  else if (isset($return->headers[$header_name])) {
123  $return->headers[$header_name][] = $value['value'];
124  }
125  else {
126  $return->headers[$header_name] = $value['value'];
127  }
128  switch ($header_name) {
129  case 'content-type':
130  $content_type = $this->parseHeaderValue($value['value']);
131  if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
132  $return->ctype_primary = $regs[1];
133  $return->ctype_secondary = $regs[2];
134  }
135  if (isset($content_type['other'])) {
136  while (list($p_name, $p_value) = each($content_type['other'])) {
137  $return->ctype_parameters[$p_name] = $p_value;
138  }
139  }
140  break;
141  case 'content-disposition';
142  $content_disposition = $this->parseHeaderValue($value['value']);
143  $return->disposition = $content_disposition['value'];
144  if (isset($content_disposition['other'])) {
145  while (list($p_name, $p_value) = each($content_disposition['other'])) {
146  $return->d_parameters[$p_name] = $p_value;
147  }
148  }
149  break;
150  case 'content-transfer-encoding':
151  $content_transfer_encoding = $this->parseHeaderValue($value['value']);
152  break;
153  }
154  }
155  if (isset($content_type))
156  {
157  $ctype = strtolower($content_type['value']);
158 
159  if ($ctype == 'application/xop+xml') {
160  $ctype = 'application/xml';
161  }
162 
163  switch ($ctype)
164  {
165  case 'application/xop+xml'://Add by dpascual
166  $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
167  $parts= array();
168  if (!isset($content_type['other']['boundary']))
169  {
170  $content_type['other']['boundary']='MIME_Boundary';
171  $parts = $this->boundarySplit($body, $content_type['other']['boundary']);
172  }
173  if (count($parts)==0)//Si no se encuentra partes con la cadena Mime probamos con uuid
174  {
175  $content_type['other']['boundary']='uuid:';
176  $parts = $this->boundarySplit($body, $content_type['other']['boundary']);
177  }
178 
179  for ($i = 0; $i < count($parts); $i++)
180  {
181  list($part_header, $part_body) = $this->splitBodyHeader($parts[$i]);
182  $return->parts[] = $this->doDecode($part_header, $part_body, $default_ctype);
183  }
184  $return->body = $body;
185  break;
186  case 'text/plain':
187  $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
188  if ($this->params['include_bodies']) {
189  $return->body = $this->params['decode_bodies'] ? WSCMime::decode($body, $encoding) : $body;
190  }
191  break;
192  case 'text/html':
193  $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
194  if ($this->params['include_bodies']) {
195  $return->body = $this->params['decode_bodies'] ? WSCMime::decode($body, $encoding) : $body;
196  }
197  break;
198  case 'multipart/digest':
199  case 'multipart/alternative':
200  case 'multipart/related':
201  case 'multipart/mixed':
202  case 'multipart/signed':
203  case 'multipart/encrypted':
204  if (!isset($content_type['other']['boundary'])) {
205  return false;
206  }
207  $default_ctype = $ctype === 'multipart/digest' ? 'message/rfc822' : 'text/plain';
208  $parts = $this->boundarySplit($body, $content_type['other']['boundary']);
209  for ($i = 0; $i < count($parts); $i++) {
210  list($part_header, $part_body) = $this->splitBodyHeader($parts[$i]);
211  $return->parts[] = $this->doDecode($part_header, $part_body, $default_ctype);
212  }
213  break;
214  case 'message/rfc822':
215  $obj = new WSCMimeParser($this->params);
216  $return->parts[] = $obj->decode($body, false);
217  unset($obj);
218  break;
219  default:
220  if ($this->params['include_bodies']) {
221  $return->body = $this->params['decode_bodies'] ? WSCMime::decode($body, $content_transfer_encoding['value']) : $body;
222  }
223  break;
224  }
225  }
226  else {
227  $ctype = explode('/', $default_ctype);
228  $return->ctype_primary = $ctype[0];
229  $return->ctype_secondary = $ctype[1];
230  if ($this->params['include_bodies']) {
231  $return->body = $this->params['decode_bodies'] ? WSCMime::decode($body) : $body;
232  }
233  }
234  return $return;
235  }
236 
237 
247  protected function splitBodyHeader($input)
248  {
249  $pos = strpos($input, $this->params['crlf'] . $this->params['crlf']);
250  if ($pos === false) {
251  return false;
252  }
253  $crlf_len = strlen($this->params['crlf']);
254  $header = substr($input, 0, $pos);
255  $body = substr($input, $pos + 2 * $crlf_len);
256  if (substr_compare($body, $this->params['crlf'], -$crlf_len) === 0) {
257  $body = substr($body, 0, -$crlf_len);
258  }
259  return array($header, $body);
260  }
268  protected function parseHeaders($input)
269  {
270  $return = array();
271  if ($input !== '') {
272  // Unfold the input
273  $input = preg_replace('/' . $this->params['crlf'] . "(\t| )/", ' ', $input);
274  $headers = explode($this->params['crlf'], trim($input));
275  foreach ($headers as $value) {
276  $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
277  $hdr_value = substr($value, $pos+1);
278  if ($hdr_value[0] == ' ') {
279  $hdr_value = substr($hdr_value, 1);
280  }
281  $return[] = array(
282  'name' => $hdr_name,
283  'value' => $this->params['decode_headers'] ? $this->decodeHeader($hdr_value) : $hdr_value,
284  );
285  }
286  }
287  return $return;
288  }
298  protected function parseHeaderValue($input)
299  {
300  $return = array();
301  $matches = array();
302  $parts = preg_split('/;\s*/', $input);
303  if (!empty($parts)) {
304  $return['value'] = trim($parts[0]);
305  for ($n = 1; $n < count($parts); $n++) {
306  if (preg_match_all('/(([[:alnum:]]+)="?([^"]*)"?\s?;?)+/i', $parts[$n], $matches)) {
307  for ($i = 0; $i < count($matches[2]); $i++) {
308  $return['other'][strtolower($matches[2][$i])] = $matches[3][$i];
309  }
310  }
311  }
312  }
313  else {
314  $return['value'] = trim($input);
315  }
316  return $return;
317  }
326  protected function boundarySplit($input, $boundary)
327  {
328  $parts = array();
329  $tmp = explode('--' . $boundary, $input);
330  for ($i = 1; $i < count($tmp)-1; $i++) {
331  $parts[] = $tmp[$i];
332  }
333  return $parts;
334  }
344  protected function decodeHeader($input)
345  {
346  return WSCMime::decode_mime_string($input, $this->params['default_charset']);
347  }
348 
360  protected function structure_part(\stdClass $part, $count = 0, $parent = '')
361  {
362  $struct = new WSCMessagePart();
363 
364  $mimeId = isset($part->mime_id)?$part->mime_id:FALSE;
365  $struct->mime_id = $mimeId?: (empty($parent) ? (string)$count : "$parent.$count");
366 
367  $headers = isset($part->headers)?$part->headers:null;
368  $struct->headers = $headers;
369 
370  $ctype_primary = isset($part->ctype_primary)?$part->ctype_primary:null;
371  $struct->ctype_primary = $ctype_primary;
372 
373  $ctype_secondary = isset($part->ctype_secondary)?$part->ctype_secondary:null;;
374  $struct->ctype_secondary = $ctype_secondary;
375 
376  $ctype_parameters = isset($part->ctype_parameters)?$part->ctype_parameters:null;
377  $struct->ctype_parameters = $ctype_parameters;
378 
379  $struct->mimetype = $ctype_primary . '/' . $ctype_secondary;
380 
381 
382  if (isset($struct->headers['content-transfer-encoding']))
383  {
384  $struct->encoding = $struct->headers['content-transfer-encoding'];
385  }
386 
387  if (isset($struct->ctype_parameters['charset']))
388  {
389  $struct->charset = $struct->ctype_parameters['charset'];
390  }
391 
392  //Determina filename
393  $filename = '';
394  if (isset($part->d_parameters))
395  {
396  if (isset($part->d_parameters['filename']))
397  {
398  $filename = $part->d_parameters['filename'];
399  if (!$this->params['decode_headers'])
400  {
401  $filename = $this->decodeHeader($filename);
402  }
403  }
404  }
405 
406  if (empty($filename) && (isset($part->ctype_parameters)))
407  {
408  if (isset($part->ctype_parameters['name']))
409  {
410  $filename = $part->ctype_parameters['name'];
411  if (!$this->params['decode_headers'])
412  {
413  $filename = $this->decodeHeader($filename);
414  }
415  }
416  }
417  $struct->filename = $filename;
418 
419  $struct->body = $part->body;
420  $struct->size = strlen($part->body);
421  //Disposition
422  $disposition = isset($part->disposition)?$part->disposition:'';
423  $struct->disposition = $disposition;
424  $count = 0;
425 
426  $parts = isset($part->parts)?(array)$part->parts:array();
427 
428  foreach ($parts as $child_part)
429  {
430  $struct->parts[] = $this->structure_part($child_part, ++$count, $struct->mime_id);
431  }
432  return $struct;
433  }
434 }
__construct($params=array())
boundarySplit($input, $boundary)
decodeMTOM($vData, $convert=true)
structure_part(\stdClass $part, $count=0, $parent='')
doDecode($headers, $body, $default_ctype='text/plain')
decode($input, $convert=true)