3 * File containing the ezcMailMultipartParser class
7 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
8 * @license http://ez.no/licenses/new_bsd New BSD License
12 * Base class for Multipart parsers.
18 abstract class ezcMailMultipartParser
extends ezcMailPartParser
21 * The boundary separator string.
25 private $boundary = null;
28 * The headers for the multipart.
30 * @var ezcMailHeadersHolder
32 protected $headers = null;
35 * The headers for the current subpart.
37 * @var ezcMailHeadersHolder
39 private $currentPartHeaders = null;
44 * @var ezcMailPartParser
46 private $currentPartParser = null;
49 * This state is used prior to hitting the first part.
51 const PARSE_STATE_PRE_FIRST
= 1;
54 * This state is used when the parser is parsing headers.
56 const PARSE_STATE_HEADERS
= 2;
59 * This state is used when the parser is parsing the body.
61 const PARSE_STATE_BODY
= 3;
64 * This state is set after the last of the parts is closed.
66 const PARSE_STATE_POST_LAST
= 4;
69 * Stores the state of the parser.
73 private $parserState = self
::PARSE_STATE_PRE_FIRST
;
76 * Constructs a new Multipart parser.
78 * @param ezcMailHeadersHolder $headers
80 public function __construct( ezcMailHeadersHolder
$headers )
82 $this->headers
= $headers;
85 preg_match( '/\s*boundary="?([^;"]*);?/i',
86 $this->headers
['Content-Type'],
88 if ( count( $parameters ) > 0 )
90 $this->boundary
= trim( $parameters[1], '"' );
94 // no boundary?!? Houston, we have a problem.
95 // todo: try to detect the boundary by scanning for --lines
100 * Parses a multipart body.
102 * @throws ezcBaseFileNotFoundException
103 * if a neccessary temporary file could not be opened.
104 * @param string $origLine
106 public function parseBody( $origLine )
108 if ( $this->parserState
== self
::PARSE_STATE_POST_LAST
)
113 $line = rtrim( $origLine, "\r\n" );
115 // check if we hit any of the boundaries
117 $endOfMultipart = false;
118 if ( strlen( $line ) > 0 && $line[0] == "-" )
120 if ( strcmp( trim( $line ), '--' . $this->boundary
) === 0 )
124 else if ( strcmp( trim( $line ), '--' . $this->boundary
. '--' ) === 0 )
126 $endOfMultipart = true;
130 // actions to do when starting or finishing a part
131 if ( $newPart ||
$endOfMultipart )
133 if ( $this->parserState
!= self
::PARSE_STATE_BODY
)
135 // something is b0rked, we got a new separator before getting a body
136 // we'll skip this part and continue to the next
137 $this->currentPartParser
= null;
138 $this->currentPartHeaders
= new ezcMailHeadersHolder();
139 $this->parserState
= $newPart ? self
::PARSE_STATE_HEADERS
: self
::PARSE_STATE_POST_LAST
;
143 // complete the work on the current part if there was any
144 if ( $this->currentPartParser
!== null )
146 $part = $this->currentPartParser
->finish();
147 if ( $part !== null ) // parsing failed
149 $this->partDone( $part );
153 // prepare for a new part if any
154 $this->currentPartParser
= null;
155 $this->parserState
=self
::PARSE_STATE_POST_LAST
;
158 $this->parserState
= self
::PARSE_STATE_HEADERS
;
159 $this->currentPartHeaders
= new ezcMailHeadersHolder();
163 // normal data, pass to headers or current body
166 if ( $this->parserState
== self
::PARSE_STATE_HEADERS
&& $line == '' )
168 $this->currentPartParser
= self
::createPartParserForHeaders( $this->currentPartHeaders
);
169 $this->parserState
= self
::PARSE_STATE_BODY
;
171 else if ( $this->parserState
== self
::PARSE_STATE_HEADERS
)
173 $this->parseHeader( $line, $this->currentPartHeaders
);
175 else if ( $this->parserState
== self
::PARSE_STATE_BODY
)
177 if ( $this->currentPartParser
) // we may have none if the part type was unknown
179 // send body data to the part
180 $this->currentPartParser
->parseBody( $origLine );
183 // we are done parsing the multipart, ignore anything else pushed to us.
188 * Completes the parsing of the multipart and returns the corresponding part.
190 * This method should not be overriden. Use finishMultipart() instead.
192 * @return ezcMailMultipart
194 public function finish()
196 if ( $this->parserState
!= self
::PARSE_STATE_POST_LAST
)
198 // this should never happen
199 // let's give the last parser a chance to clean up after himself
200 if ( $this->currentPartParser
!== null )
202 $part = $this->currentPartParser
->finish();
203 $this->partDone( $part );
204 $this->currentPartParser
= null;
207 $multipart = $this->finishMultipart();
208 ezcMailPartParser
::parsePartHeaders( $this->headers
, $multipart );
209 $multipart->boundary
= $this->boundary
;
214 * This function will be called every time a part has been parsed.
216 * Implementors should put the part into the correct multitype part.
217 * @param ezcMailPart $part
219 abstract public function partDone( ezcMailPart
$part );
222 * Returns the multipart part corresponding to the parsed object.
224 * This method is called by finish() when all parts have been parsed.
226 * @return ezcMailMultipart
228 abstract public function finishMultipart();