/**
* Recursively parse BODYSTRUCTURE data from a FETCH return (see
* RFC 3501 [7.4.2]).
*
* @param Horde_Imap_Client_Tokenize $data Data returned from the server.
*
* @return Horde_Mime_Part Mime part object.
*/
protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data)
{
$ob = new Horde_Mime_Part();
// If index 0 is an array, this is a multipart part.
if (($entry = $data->next()) === true) {
do {
$ob->addPart($this->_parseBodystructure($data));
} while (($entry = $data->next()) === true);
// The subpart type.
$ob->setType('multipart/' . $entry);
// After the subtype is further extension information. This
// information MAY appear for BODYSTRUCTURE requests.
// This is parameter information.
if (($tmp = $data->next()) === false) {
return $ob;
} elseif ($tmp === true) {
foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
$ob->setContentTypeParameter($key, $val);
}
}
} else {
$ob->setType($entry . '/' . $data->next());
if ($data->next() === true) {
foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
$ob->setContentTypeParameter($key, $val);
}
}
if (!is_null($tmp = $data->next())) {
$ob->setContentId($tmp);
}
if (!is_null($tmp = $data->next())) {
$ob->setDescription(Horde_Mime::decode($tmp));
}
if (!is_null($tmp = $data->next())) {
$ob->setTransferEncoding($tmp);
}
$ob->setBytes($data->next());
// If the type is 'message/rfc822' or 'text/*', several extra
// fields are included
switch ($ob->getPrimaryType()) {
case 'message':
if ($ob->getSubType() == 'rfc822') {
if ($data->next() === true) {
// Ignore: envelope
$data->flushIterator(false);
}
if ($data->next() === true) {
$ob->addPart($this->_parseBodystructure($data));
}
$data->next();
// Ignore: lines
}
break;
case 'text':
$data->next();
// Ignore: lines
break;
}
// After the subtype is further extension information. This
// information MAY appear for BODYSTRUCTURE requests.
// Ignore: MD5
if ($data->next() === false) {
return $ob;
}
}
// This is disposition information
if (($tmp = $data->next()) === false) {
return $ob;
} elseif ($tmp === true) {
$ob->setDisposition($data->next());
if ($data->next() === true) {
foreach ($this->_parseStructureParams($data, 'content-disposition') as $key => $val) {
$ob->setDispositionParameter($key, $val);
}
}
$data->next();
}
// This is language information. It is either a single value or a list
// of values.
if (($tmp = $data->next()) === false) {
return $ob;
} elseif (!is_null($tmp)) {
$ob->setLanguage($tmp === true ? $data->flushIterator() : $tmp);
}
// Ignore location (RFC 2557) and consume closing paren.
$data->flushIterator(false);
return $ob;
}