Skip to content

Commit

Permalink
Boundary detection overhauled to support "related" and "alternative" #90
Browse files Browse the repository at this point in the history
  • Loading branch information
Webklex committed Jan 22, 2021
1 parent b2c749f commit 5941cee
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 42 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
- Boundary detection simplified #90
- Prevent potential body overwriting #90
- CSV files are no longer regarded as plain body
- Boundary detection overhauled to support "related" and "alternative" multipart messages #90

### Added
- NaN

### Affected Classes
- [Structure::class](src/Structure.php)
- [Message::class](src/Message.php)
- [Header::class](src/Header.php)
- [Part::class](src/Part.php)

### Breaking changes
- NaN
Expand Down
25 changes: 25 additions & 0 deletions src/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,31 @@ public function find($pattern) {
return null;
}

/**
* Try to find a boundary if possible
*
* @return string|null
*/
public function getBoundary(){
$boundary = $this->find("/boundary\=(.*)/i");

This comment has been minimized.

Copy link
@MouMoutMan

MouMoutMan Apr 16, 2021

This regex update is a regression... See #126 ;)


if ($boundary === null) {
return null;
}

return $this->clearBoundaryString($boundary);
}

/**
* Remove all unwanted chars from a given boundary
* @param string $str
*
* @return string
*/
private function clearBoundaryString($str) {
return str_replace(['"', '\r', '\n', "\n", "\r", ";", "\s"], "", $str);
}

/**
* Parse the raw headers
*
Expand Down
90 changes: 48 additions & 42 deletions src/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ protected function parse(){
* Determine the message content type
*/
public function findContentType(){

$content_type = $this->header->get("content_type");
$content_type = (is_array($content_type)) ? implode(' ', $content_type) : $content_type;
if(stripos($content_type, 'multipart') === 0) {
Expand All @@ -97,28 +96,51 @@ public function findContentType(){
}

/**
* Determine the message content type
* Find all available headers and return the left over body segment
* @var string $context
* @var integer $part_number
*
* @return string|null
* @return Part[]
* @throws InvalidMessageDateException
*/
public function getBoundary(){
$boundary = $this->header->find("/boundary\=(.*)/i");

if ($boundary === null) {
return null;
private function parsePart($context, $part_number = 0){
$body = $context;
while (($pos = strpos($body, "\r\n")) > 0) {
$body = substr($body, $pos + 2);
}
$headers = substr($context, 0, strlen($body) * -1);
$body = substr($body, 0, -2);

return $this->clearBoundaryString($boundary);
$headers = new Header($headers);
if (($boundary = $headers->getBoundary()) !== null) {
return $this->detectParts($boundary, $body, $part_number);
}
return [new Part($body, $headers, $part_number)];
}

/**
* Remove all unwanted chars from a given boundary
* @param string $str
* @param string $boundary
* @param string $context
* @param int $part_number
*
* @return string
* @return array
* @throws InvalidMessageDateException
*/
private function clearBoundaryString($str) {
return str_replace(['"', '\r', '\n', "\n", "\r", ";", "\s"], "", $str);
private function detectParts($boundary, $context, $part_number = 0){
$base_parts = explode( $boundary, $context);
$final_parts = [];
foreach($base_parts as $ctx) {
$ctx = substr($ctx, 2);
if ($ctx !== "--" && $ctx != "") {
$parts = $this->parsePart($ctx, $part_number);
foreach ($parts as $part) {
$final_parts[] = $part;
$part_number = $part->part_number;
}
$part_number++;
}
}
return $final_parts;
}

/**
Expand All @@ -130,39 +152,23 @@ private function clearBoundaryString($str) {
*/
public function find_parts(){
if($this->type === IMAP::MESSAGE_TYPE_MULTIPART) {
if (($boundary = $this->getBoundary()) === null) {
if (($boundary = $this->header->getBoundary()) === null) {
throw new MessageContentFetchingException("no content found", 0);
}

$boundaries = [
$boundary
];

if (preg_match("/boundary\=\"?(.*)\"?/", $this->raw, $match) == 1) {
if(is_array($match[1])){
foreach($match[1] as $matched){
$boundaries[] = $this->clearBoundaryString($matched);
}
}else{
if(!empty($match[1])) {
$boundaries[] = $this->clearBoundaryString($match[1]);
}
}
}

$raw_parts = explode( $boundaries[0], str_replace($boundaries, $boundaries[0], $this->raw) );
$parts = [];
$part_number = 0;
foreach($raw_parts as $part) {
$part = trim(rtrim($part));
if ($part !== "--") {
$parts[] = new Part($part, null, $part_number);
$part_number++;
}
}
return $parts;
return $this->detectParts($boundary, $this->raw);
}

return [new Part($this->raw, $this->header)];
}

/**
* Try to find a boundary if possible
*
* @return string|null
* @Depricated since version 2.4.4
*/
public function getBoundary(){
return $this->header->getBoundary();
}
}

0 comments on commit 5941cee

Please sign in to comment.